Contents
- Domma CMS User Manual
- 1. Using the CMS
- 2. Tutorials
- 3. Components
- 4. API Reference
- 5. Tools
Components How-To
Updated by Darryl Waterhouse on 29 September 2026 · 3 min read
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.
Create a component
- Open Data > Components and click New component in the banner.
- Name it in lowercase-with-hyphens (e.g.
price-tag). The name is permanent and becomes the tag<dm-price-tag>. - Fill the
<template>,<props>and<script>tabs, and<style>if you want one (the live preview updates as you type). - Save. It compiles first - fix any reported errors and it lands on disk.
Render a value
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" /]
Add state and respond to a click
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>
Initialise state from a prop
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 }); }
Show something conditionally
{{#if showReset}}<button data-action="reset">Reset</button>{{/if}}
{{#unless soldOut}}<button>Buy</button>{{/unless}}
Loop over a list
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>
Accept arbitrary inner content (slot)
<template>
<div class="dm-callout"><slot></slot></div>
</template>
<dm-callout><p>Anything here is projected into the slot.</p></dm-callout>
Fetch data from the API
Domma's HTTP helper H is available inside the script. Fetch in onMount and push results into state. /api/v1/<slug> answers only if the collection's API read access allows the visitor (collection editor, API & Export tab):
export default {
data() { return { entries: [] }; },
async onMount() {
const res = await H.get('/api/v1/announcements');
this.set({ entries: res.entries || [] });
}
};
Run something on a timer (and clean up)
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().toTimeString().slice(0, 8) }); } }, // HH:mm:ss
onMount() { this.tick(); this._timer = setInterval(() => this.tick(), 1000); },
onUnmount() { if (this._timer) clearInterval(this._timer); }
};
Style it without leaking
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>
Use it on a page
Either form works and auto-loads the component's module:
[component name="counter" step="2" /]
<dm-counter step="2"></dm-counter>
Move a component between sites
- On Components, right-click the row (or use its menu) and choose Export .dmcomponent.json - you'll download
name.dmcomponent.json. The editor has the same Export in its banner. - On the other site, click Import in the Components banner and pick that file. On a name clash you'll be asked to confirm an overwrite.
File it under a project
In the editor, set the Project dropdown. That writes project to the component's .meta.json sidecar, so it shows in that project's Components list (#/projects/<slug>/components).
Next: Walkthrough → · Rules & gotchas
On this page
- Create a component
- Render a value
- Add state and respond to a click
- Initialise state from a prop
- Show something conditionally
- Loop over a list
- Accept arbitrary inner content (slot)
- Fetch data from the API
- Run something on a timer (and clean up)
- Style it without leaking
- Use it on a page
- Move a component between sites
- File it under a project