Config and theme
How app config, themes, and settings resolve.
The app config is one JSON document with a handful of top-level entities. Everything a screen renders resolves from these, in a defined order.
The config entities
| Entity | What it holds |
|---|---|
screens | Every screen, keyed by screenId, each with its ordered block list |
| blocks | Per-instance source, style, and options, inside each screen entry |
| theme | Shared visual styling: per-block-type style entries and common style keys |
settings | Global defaults for source and options, keyed by block type |
integrations | Per-integration configuration: API keys, flags, endpoints |
Blocks are not stored separately; they live inside their screen's layout. The theme and settings exist so you don't repeat the same styling and defaults on every instance.
Style resolution
When a block asks for its style (via useBlockStyle), three layers deep-merge, later layers winning:
- Theme style — a shared theme entry the block opts into (for example, a common
product-collectionkey several block types reuse). - Theme block style — the theme's entry for this block type: the key
componentId--variantif the block has a variant and the theme defines it, otherwisecomponentId. - Block style — the instance's own
styleobject from its config entry.
So the theme sets the look once per block type, a variant refines it, and an individual instance overrides only what it needs. An instance with an empty style simply inherits the theme result.
Settings resolution
source and options resolve the same way (via useBlockSettings), with global settings playing the role the theme plays for styles:
- Global settings — the
settingsentry undercomponentId--variant, falling back tocomponentId. These are defaults for every instance of the type. - Block settings — the instance's own
sourceandoptions, which override the globals.
A block can also extend another type's settings entry (for example, a recommendations block inheriting product-collection defaults); those sit below the globals in the merge. As a rule of thumb, the closer a value sits to the instance, the higher its precedence.
{
"settings": {
"product-card": { "options": { "showVendor": false } }
}
}With this global entry, every product-card hides the vendor, except an instance whose own options set "showVendor": true.
Drafts and the live theme
An app has one live theme (the config installed apps actually fetch) and any number of draft themes. Edits always target a draft: you can restructure screens, move blocks, and change styles without affecting users. When the draft is ready, an operator publishes it, and it becomes the live config for every installed app on the next fetch.
Never edit the live theme directly
Config mistakes on the live theme reach real users immediately. Make changes on a draft, verify them, and let publishing be the deliberate step that ships them.
Config changes need no app-store release because publishing a theme is a backend event that apps pick up on launch. See Architecture for the two release tracks, and State and data for the hooks that read these resolved values.

