Rule of thumb
One heading, one image, one link → primitive. Hero, pricing table, slider, multi-field card → component.
Comparison
| Name | Type | Description |
|---|---|---|
Storage | — | Primitive: typed node value. Component: JSON payload (+ optional slot children). |
Markup | — | Primitive: native HTML. Component: host tag + render()-built DOM. |
Schema | — | Primitive: built-in. Component: fields[] synced to node_definitions. |
Editing | — | Primitive: inline / specialized panels. Component: form + optional custom sidebar. |
Primitives
<h1 data-macro-key="hero.title">Build faster</h1>
<img data-macro-key="hero.image" src="/hero.jpg" alt="Hero" />
<a data-macro-key="hero.cta" href="/signup">Get started</a>- Macro owns DOM bindings and overlay UIs for built-ins.
- Tag inference picks
macro/text,macro/image,macro/link, … - Best for marketing copy, simple media, and form field values.
Deep dive: Primitive reference.
Components
<acme-hero
data-macro-key="home.hero"
data-macro-type="@acme/hero"
></acme-hero>- You own HTML structure and
render(). - One JSON payload per key + locale;
fields[]drive the sidebar. - Optional slots for repeaters or projected Light DOM.
Guide: Custom components.
Decision guide
Do
- Start with primitives; extract a component when fields and layout grow together.
- Prefer Store components when a validated bundle already solves the block.
- Use items slots for editor-managed lists inside a component shell.
Don’t
- Don’t wrap a lone H1 in a custom component “for consistency”.
- Don’t put unrelated page copy into one giant component payload.
- Don’t fork Store bundles in place — use slots or design fields.
Content vs styled (components)
Structure and behavior only — inherit the host site. At most a few tokens (--macro-accent, --macro-radius). Reference: content-slider.