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:
| Plane | Theme source | Built output | Role |
|---|---|---|---|
| Schema | schema.ts overlay | dist/schema.json | Editor chrome: pages, layouts, root props, built-in overrides |
| Library | library/components/ | dist/schema.library.json (embedded on the schema as library) | Optional drag-drop extras |
| Data | pages/, props.json | dist/data.json | Live instances the store renders |
And the editor's "add component" surface merges three catalogs with different insert semantics:
| Catalog | Storage | Insert mode |
|---|---|---|
| Lithium built-ins | Platform components registry | Places the registry type with defaults |
| Theme library | schema.library.json | Inline copy of the entry |
| My library / Marketplace | Backend template_components | type: "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| dataTheme 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": [...] }):
| Field | Source | Role |
|---|---|---|
id | libraryId / id in meta, else the file or folder name | Stable identity in the catalog |
title, subtitle | meta | Editor card text |
category | meta (defaults to "library") | Grouping in the picker |
tags | meta | Search |
imageUrl | meta | Card preview image |
type | meta (defaults to "custom") | What gets inserted — may also be a built-in registry type |
code | compiled from the .tsx | The custom JSX string |
propsSchema | meta | Inspector definition, seeded on drop |
propsDefault | meta props | Initial prop values on drop |
slotsSchema, slotsDefault, slotsLayout, children | meta / folder | Seeded on drop for slotted or layout entries |
Build it with the rest of the theme:
npm run build # feeef template build → dist/schema.library.jsonWhat 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:
type (usually custom) in the target section.code, propsSchema, and any slot definitions onto the node.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
| Bad | Good |
|---|---|
library/components/x/component.json + component.jsx for a leaf | Flat 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/footer | shared/components/ + $ref |
| Expecting a dropped entry to update when the theme updates | Drops 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.