Short, copy-paste recipes for the things you'll actually do. For a guided build-from-scratch see the Walkthrough; for the full API see the Reference.
price-tag). The name is permanent and
becomes the tag <dm-price-tag>.<template>, <props> and <script>
tabs (the editor watches the live preview as you type).Interpolate a prop or state value with double braces:
<template><span class="dm-price">{{currency}}{{amount}}</span></template>
<props>
{
"currency": { "type": "string", "default": "£" },
"amount": { "type": "number", "default": 0 }
}
</props>
<script>export default {};</script>
Use it: [component name="price-tag" currency="$" amount="49" /]
Render the markup with data-action hooks, then attach one delegated
listener to the shadow root in onMount. Mutate state with this.set() - the
template re-renders automatically.
<template>
<div class="dm-counter">
<strong>{{count}}</strong>
<button data-action="inc">+</button>
</div>
</template>
<props>{ "step": { "type": "number", "default": 1 } }</props>
<script>
export default {
data() { return { count: 0 }; },
methods: {
inc() { this.set({ count: this.data.count + this.props.step }); }
},
onMount() {
this.el.shadowRoot.addEventListener('click', (e) => {
if (e.target.dataset.action === 'inc') this.inc();
});
}
};
</script>
data() runs without a this binding, so it can't read props. Seed the value
in onMount() instead:
data() { return { count: 0 }; },
onMount() { this.set({ count: this.props.initial }); }
{{#if showReset}}<button data-action="reset">Reset</button>{{/if}}
{{#unless soldOut}}<button>Buy</button>{{/unless}}
Pass an array prop (attribute value is JSON) and iterate with {{#each}}:
<template>
<ul>{{#each items}}<li>{{@index}}. {{.}}</li>{{/each}}</ul>
</template>
<props>{ "items": { "type": "array", "default": [] } }</props>
<script>export default {};</script>
<dm-my-list items='["Alpha","Beta","Gamma"]'></dm-my-list>
<template>
<div class="dm-callout"><slot></slot></div>
</template>
<dm-callout><p>Anything here is projected into the slot.</p></dm-callout>
Domma's HTTP helper H is available inside the script. Fetch in onMount and
push results into state:
export default {
data() { return { entries: [] }; },
async onMount() {
const res = await H.get('/api/v1/announcements');
this.set({ entries: res.entries || [] });
}
};
Start the interval in onMount; always tear it down in onUnmount so it
doesn't leak when the element is removed:
export default {
data() { return { now: '' }; },
_timer: null,
methods: { tick() { this.set({ now: new Date().toLocaleTimeString() }); } },
onMount() { this.tick(); this._timer = setInterval(() => this.tick(), 1000); },
onUnmount() { if (this._timer) clearInterval(this._timer); }
};
Anything in <style> is scoped to the component, so you can use short class names
freely:
<style>
.dm-callout { padding: 1rem; border-left: 3px solid var(--dm-primary, #5aa8ff); }
</style>
Either form works and auto-loads the component's module:
[component name="counter" step="2" /]
<dm-counter step="2"></dm-counter>
name.dmcomponent.json.In the editor, set the Project dropdown. That writes meta.project to
the sidecar so the component shows under that project's filtered list and is scoped accordingly.
Next: Walkthrough → · Rules & gotchas