.dev
Storefront templates

Schema library

The theme's drag-drop catalog — declare optional block presets in library/components/ and merchants add them from the editor.

A theme is more than what renders on install. schema.library.json is the theme's drag-drop catalog: extra blocks — promo banners, FAQ sections, countdown bars — that ship with the theme but stay out of the pages until the merchant adds them from the editor. Entries here may be entirely unused in data.json; that is the point.

Three planes, three catalogs

The library sits between the editor schema and the page data:

PlaneTheme sourceBuilt outputRole
Schemaschema.ts overlaydist/schema.jsonEditor chrome: pages, layouts, root props, built-in overrides
Librarylibrary/components/dist/schema.library.json (embedded on the schema as library)Optional drag-drop extras
Datapages/, props.jsondist/data.jsonLive instances the store renders

And the editor's "add component" surface merges three catalogs with different insert semantics:

CatalogStorageInsert mode
Lithium built-insPlatform components registryPlaces the registry type with defaults
Theme libraryschema.library.jsonInline copy of the entry
My library / MarketplaceBackend template_componentstype: "reference" + refId (live pointer)
flowchart LR
  schemaTs["schema.ts overlay"]
  lithium["Lithium built-ins"]
  disk["library/components/*.tsx"]
  build["feeef template build"]
  schema["dist/schema.json + library"]
  editor["Merchant editor — Theme tab"]
  data["templateData placement"]
  schemaTs --> build
  lithium --> build
  disk --> build
  build --> schema
  schema --> editor
  editor -->|copy-on-drop| data

Theme library inserts are copies, not references. Once dropped, the node lives in the store's templateData and later changes to the library entry do not propagate. Chrome that must stay consistent across pages belongs in shared/components/ instead.

Authoring entries

Same flat-TSX rules as everywhere else in the kit:

library/components/
  promo-banner.tsx     # preferred: flat leaf — export const meta + function App()
  rich-block/          # folder only when slots/ or children/ are needed
    rich-block.tsx
    slots/body/...

A minimal entry:

const propsSchema = {
  message: { type: "string", name: "Message" },
} as const;

export const meta = {
  type: "custom",
  title: "Promo banner",
  subtitle: "Thin bar above the header",
  category: "marketing",
  tags: ["promo", "banner"],
  imageUrl: "https://.../preview.webp",
  propsSchema,
  props: { message: "Free shipping this week" },
} as const;

function App() {
  return (
    <div style={{ background: "hsl(var(--primary))", color: "hsl(var(--primary-foreground))" }}>
      {props.message}
    </div>
  );
}

Catalog entry structure

The build compiles each disk entry into this shape inside dist/schema.library.json ({ "version": "1.0", "components": [...] }):

FieldSourceRole
idlibraryId / id in meta, else the file or folder nameStable identity in the catalog
title, subtitlemetaEditor card text
categorymeta (defaults to "library")Grouping in the picker
tagsmetaSearch
imageUrlmetaCard preview image
typemeta (defaults to "custom")What gets inserted — may also be a built-in registry type
codecompiled from the .tsxThe custom JSX string
propsSchemametaInspector definition, seeded on drop
propsDefaultmeta propsInitial prop values on drop
slotsSchema, slotsDefault, slotsLayout, childrenmeta / folderSeeded on drop for slotted or layout entries

Build it with the rest of the theme:

npm run build   # feeef template build → dist/schema.library.json

What the merchant sees

In the merchant editor's component picker, the theme's library entries appear as cards — title, subtitle, preview image, grouped by category and filterable by tags. Dropping one onto a page:

Creates a new node of the entry's type (usually custom) in the target section.
Copies code, propsSchema, and any slot definitions onto the node.
Seeds props from propsDefault, then opens the normal inspector so the merchant can edit them.

From then on it behaves like any hand-placed component — merchants restyle or delete it without affecting the catalog.

Publish behavior

When you publish, your shared/ and library/ components are also uploaded as backend template_components rows — that is what powers the "My library" catalog and marketplace component reuse. The theme's own schema.library stays embedded on the listing's schema, so merchants who install the theme get the drag-drop catalog with it — no separate fetch involved.

Pitfalls

BadGood
library/components/x/component.json + component.jsx for a leafFlat library/components/x.tsx with export const meta
Duplicating library JSX into every page "so it shows by default"Ship default placements in pages/; keep the library for optional extras
Using the library for the site header/footershared/components/ + $ref
Expecting a dropped entry to update when the theme updatesDrops are copies — version the design in shared/ or the backend library

Related: custom components for the TSX contract, data model for what lands in templateData.

On this page