Components Rules
Components
The constraints the compiler enforces, and the handful of behaviours that commonly trip people up. Everything here is validated at save time — a component that breaks a rule never reaches disk.
- Names must match
^[a-z][a-z0-9-]*$— start with a lowercase letter, then lowercase letters, digits and hyphens. - Valid:
counter,copy-button,price-tag-2. Invalid:Counter,1counter,my_component,../etc/passwd. - The name becomes the element tag
<dm-name>and is permanent — it can't be changed after creation. To rename, create a new component and delete the old one.
<template>,<props>and<script>are required. A missing one fails withMISSING_BLOCK.<style>is optional and defaults to empty.- Blocks are matched by their tags and may appear in any order.
- The
<props>body must be valid JSON and an object (not an array, not a bare value) — otherwiseINVALID_PROPS_JSON. - Every prop must declare a
typethat is one ofstring,number,boolean,array,object. Anything else (or a missing type) fails validation. defaultandlabelare optional.labelonly affects the editor's preview panel.
- The
<script>body must be exactly oneexport default { … };statement — the compiler strips that prefix and embeds the object. Noexport defaultmeansSCRIPT_MISSING_EXPORT. - Use an object expression — not a class, not multiple exports. An empty
export default {};is valid for a purely presentational component.
1.
data() can't see props. It's called with no this
binding, so this.props is unavailable there. Initialise prop-derived state inside
onMount() (which is bound) using this.set(...).
2. Boolean coercion is string-based. Attribute values are strings. A boolean prop
is true only for the string
"true"; showReset="false" is still a non-empty
string and coerces to true. To switch a boolean off, set it to "true" or
omit the attribute entirely.
3. Attribute casing. On the raw custom element, camelCase props are written as
kebab-case attributes — showReset becomes show-reset. The
[component] shortcode accepts the prop name as written.
4. Array / object props are JSON in the attribute. Pass them as a JSON string —
e.g.
items='["a","b"]' — and the runtime parses them to the declared type.
5. Listen on the shadow root. A component's rendered markup lives in
this.el.shadowRoot. Attach delegated listeners there (one listener, switch on
data-action) rather than to document.
6. Clean up in
onUnmount(). Anything you start in onMount
— intervals, timeouts, external listeners — must be torn down in onUnmount so it
doesn't leak when the element is removed.
- Components contributed by a plugin show a plugin
badge, are served from memory, and cannot be edited or deleted from the admin
(attempting it returns
PLUGIN_OWNED). - You also can't save a disk component whose name a plugin already owns — pick a different name.
- A bundle must have a numeric
formatno newer than the CMS supports, plusnameandsourcestrings — otherwiseINVALID_BUNDLE. - Importing over an existing name returns
CONFLICTunless you confirm the overwrite (the Import button prompts you). - Imported source is compiled like any save, so a broken bundle is rejected with the same errors.
{{ }}interpolation is HTML-escaped, so prop values can't inject markup.- The markdown sanitiser keeps a live allowlist of
dm-*tags, refreshed whenever you save or delete a component — a component you just created is immediately usable in page content.on*handler attributes andjavascript:URLs are still stripped globally. - Saving recompiles and invalidates the cached module, so edits take effect on the next page load without a server restart.
| Code | Meaning |
|---|---|
INVALID_NAME | Name doesn't match the naming rule. |
MISSING_BLOCK | A required <template> / <props> / <script> block is absent. |
INVALID_PROPS_JSON | Props aren't a JSON object, or a prop has an invalid/missing type. |
SCRIPT_MISSING_EXPORT | The script has no export default object. |
PLUGIN_OWNED | Tried to edit/delete (or shadow) a plugin-contributed component. |
INVALID_BUNDLE / CONFLICT | Import bundle malformed, or a name collision needs overwrite confirmation. |
Back to Reference · How-To · Walkthrough