Composition
Think of a page as a tree. The page holds sections; a section can hold smaller blocks, and a block can hold blocks too. A named space where a component accepts children is a slot.
Some blocks must always be present: a product page cannot lose its buy button, for example. The SDK enforces these rules when a document is edited, so the renderer can trust the document it receives.
You can skip this page if your theme has no editable sections or nested blocks. If you are new, start with sections and add slots only when a section really needs child blocks.
Sections and blocks
| Placement | Where it can go |
|---|---|
"section" | The top level of a surface or area |
"block" | Only inside a slot |
"both" | Anywhere |
| absent | Anywhere |
The document operations enforce placement, and checkPageData — the writer's
check before saving — reports a section that may only be a block
(not-placeable). The renderer and validatePageData never reject a saved
instance for its placement: a document already saved renders as it is.
Declare a slot
A slot is a named place a component lets children go.
Code
| Rule | Effect |
|---|---|
name | "default" or a lowercase kebab-case name |
multiple: false | Keep the first child, report the rest |
allow | Only these component types; omitted means any |
max | At most this many instances (up to 50) |
required | These types must be present; each has its own min / max |
allow is applied before multiple: false, so a disallowed child never
displaces an allowed one. Narrowing allow later is a breaking change for
saved pages: children that no longer fit are dropped with disallowed-child.
A child in an undeclared slot is dropped with unknown-slot. A component with
no default slot drops children pushed as default with undeclared-children.
Render the blocks
The renderer gives every component a blocks prop. A component places each
slot where the design wants it, and each fixed block where it belongs.
Code
<Blocks> renders a slot's children through InstanceView, so load, issues,
hidden blocks and preview markers work at every depth. <slot /> still works
for default children; a component can use either mechanism.
blocks is reserved. No field may use that key.
hasBlocks(blocks, slot) is true when the slot has at least one visible block,
so a component can skip an empty wrapper. isPreview(blocks) is true in a
Wocart Studio preview, where a section with nothing to show yet may draw a
placeholder. A shopper never sees that placeholder.
Fixed blocks
A fixed block is placed by the component's own code. An editor can change its fields and fill its slots, but cannot remove, move, duplicate or hide it.
Code
Code
The block renders even when the document does not store it, using its defaults.
Its id is derived from the owner and name (fixedBlockId). Stored values for a
fixed block that the definition no longer declares are removed with
unknown-fixed; a wrong type is replaced with a default and reported
fixed-type-mismatch.
Required blocks
A required block may move, but it cannot disappear. A slot or surface lists them:
Code
The operations refuse to remove, hide or move out the last min of a required
type, or to insert past its max. A document that lacks one — an old document,
or one saved before a theme update — renders it anyway, added at the end of its
slot, and the reader reports missing-required-block.
A product or landing surface must guarantee an order form. Give the order
form's component role: "order-form" and require it through the surface's
required sections and their fixed or required blocks. validateThemeDefinition
reports a product or landing surface that does not
(order-form-not-guaranteed): a merchant could otherwise remove the only way
to order.
Code
The order form's fields are not the theme's: the store's checkout form is set in the merchant dashboard and enforced by the server. The theme supplies its markup and style.
Presets
A preset is a ready-made configuration offered on insertion: props over the field defaults, plus children for slots.
Code
Presets are validated at assembly by building them.
instantiatePreset(theme, type, key, newId) creates the instances, each with
an id from newId.
Limits
| Limit | Value |
|---|---|
| Instances per slot | 50 (THEME_LIMITS.slotInstances) |
| Sections per surface or area | 50 (THEME_LIMITS.sections) |
| Instances per document | 1,000 (THEME_LIMITS.documentInstances) |
| Nesting below a section | 6 (THEME_LIMITS.depth) |
A slot or surface max can set a lower number. The reader renders up to the
limit and reports limit-exceeded; the operations refuse to go past it.
Assembly refuses a fixed or required block that contains itself or nests deeper
than the limit.
Hidden instances
Code
The renderer skips the instance, its load and its children. The reader still
checks it, so a hidden section cannot hide a contract error. A hidden required
or fixed block is shown again by completePage; those are guarantees, not
preferences.