.dev
Storefront templates

Data model

The TemplateData contract — component nodes, propsSchema, slots vs children, and field ownership.

TemplateData is the JSON document a store renders. It is the single contract shared by the Lithium renderer (TypeScript), the merchant editor (Flutter), the AI generators, and the template kit compiler.

Root shape

{
  props?: {                      // template-level prefs
    theme?: { mode: 'light' | 'dark' | 'system', ... },
    corners?: { ... },
    ...
  },
  i18n?: {                       // theme translations (from kit locales/*.json)
    defaultLocale: string,
    locales: string[],
    messages: Record<string, Record<string, any>>,
  },
  pages: {
    [pageId: string]: {
      props: Record<string, any>,
      sections: {
        [sectionId: string]: { components: TemplateDataComponent[] }
      }
    }
  }
}

Component node

{
  type: string,                 // registry key | "custom" | "reference" | "grid" | "flex" | "container"
  instanceId?: string | null,   // stable identity (editor selection + React key)
  title?: string | null,        // editor label only

  props: Record<string, any>,   // merchant-editable values

  // Layout types only
  children?: TemplateDataComponent[] | null,

  // Custom / slotted components
  code?: string | null,         // JSX source for type:"custom"
  propsSchema?: Record<string, PropDef> | null,
  slotsSchema?: Record<string, { name?: string | null, maxChildren?: number | null }> | null,
  slots?: Record<string, TemplateDataComponent[]> | null,
  slotsLayout?: Record<string, SlotLayoutNode> | null,  // editor chrome only

  // Library placement
  refId?: string,
  refVersion?: number,
}

Field ownership

This split is what keeps developer code and merchant edits from clobbering each other:

FieldWho edits itPurpose
propsMerchant (inspector)Values matching propsSchema
codeDeveloper / AIThe component's identity — not a prop
propsSchemaDeveloper / AIDrives the inspector UI
slotsSchemaDeveloper / AIDeclares named slots
slotsMerchant (editor)What's inside each slot
slotsLayoutDeveloper / AIEditor-only responsive arrangement of slots
childrenEditorLayout types only

code, propsSchema, slotsSchema, slots and slotsLayout are top-level fields. Never nest them inside props — both the renderer and the editor break.

PropDef — what the inspector renders

Each propsSchema entry describes one editable prop:

{
  type: 'string' | 'number' | 'boolean' | 'text' | 'array' | 'object',
  tool?: { type: 'slider', min, max, step }
       | { type: 'select', options: string[] }
       | { type: 'color' } | { type: 'image' } | { type: 'code' } | { type: 'text' },
  default?: any,
  items?: PropDef,                        // for arrays
  propsSchema?: Record<string, PropDef>,  // for objects
  name?: string,                          // display label
}

Example — a hero with an editable heading, image and CTA list:

{
  "heading": { "type": "string", "name": "Heading" },
  "background": { "type": "string", "tool": { "type": "image" }, "name": "Background" },
  "ctas": {
    "type": "array",
    "name": "Buttons",
    "items": {
      "type": "object",
      "propsSchema": {
        "label": { "type": "string" },
        "href": { "type": "string" }
      }
    }
  }
}

Layout children vs slots

MechanismUsed byMeaning
children[]grid, flex, containerPlain nested components inside a layout
slots{ id: [] }custom with slotsSchemaNamed insertion points the merchant can fill; delivered to your JSX pre-rendered as props.slots[slotId]

Single-main pages (product, checkout, thank_you) use one custom shell component whose layout lives entirely in slots — the merchant rearranges the page without breaking your code.

Reference components

A reference node places a component from the shared library by id instead of inlining code:

{ "type": "reference", "refId": "cmp_...", "refVersion": 3, "props": {}, "slots": {} }

References are resolved server-side into full custom nodes before rendering (missing refs are skipped fail-soft). This is how one component source powers many placements — fix it once, every placement updates. Manage the library via the templateComponents SDK repository.

Identity rules

  • Give every node a unique, stable instanceId (e.g. custom_dawn_header_home).
  • Never regenerate ids when editing code or props — editor selection and React reconciliation depend on them.

Schema (catalog)

The TemplateSchema describes what the editor can offer:

{
  name, version, schema,
  components: { [type]: { name, category?, propsSchema?, slots? } },
  pages: { [pageId]: { name, path, propsSchema, sections: {...} } },
  propsSchema?: { ... }  // global theme props
}

Templates ship their own schema overlay (schema.ts in the kit) merged over Lithium's built-ins; the merged result is published as StoreTemplate.schema.

On this page