Shared components
Reuse one component across pages — kit-level $ref at build time vs runtime reference nodes from the component library.
The same header should not be pasted onto five pages. Feeef has two reuse layers, and they solve different problems:
| Layer | Mechanism | Resolved | Use for |
|---|---|---|---|
| Kit shared library | shared/components/ + $ref placements | At template build — inlined into data.json | Chrome inside your theme: header, footer, order-form fields |
| Runtime references | type: "reference" + refId nodes | Server-side on every render | Components published to the backend library / marketplace |
flowchart TB
subgraph authoring [Theme authoring — template kit]
sharedDisk["shared/components/footer.tsx"]
placement["pages/*/components/footer.json — $ref: shared.footer"]
build["feeef template build"]
dataJson["dist/data.json — fully inlined"]
sharedDisk --> build
placement --> build
build --> dataJson
end
subgraph runtime [Merchant runtime — Lithium]
lib["Backend template_components library"]
refNode["type: reference + refId + prop overrides"]
resolve["materialize references"]
custom["type: custom, in memory"]
lib --> resolve
refNode --> resolve
resolve --> custom
end
dataJson -->|install / save| storeMeta["store templateData"]
storeMeta --> resolveKit shared library (build time) — prefer this
Anything that appears on two or more pages lives once under shared/components/ and
is placed everywhere else with a thin stub.
Where files go
| Kind | Path |
|---|---|
| Leaf custom (preferred) | shared/components/<id>.tsx — export const meta + function App() |
| Leaf built-in stub | shared/components/<id>.json |
| With slots / children | shared/components/<id>/<id>.tsx + slots/ or children/ |
Do not ship customs as component.json + component.jsx pairs — metadata belongs in
export const meta inside the .tsx file (see
custom components).
Placing a shared component
A placement is a small JSON stub next to the page's other components — never a copy of the JSX:
{
"order": 0,
"$ref": "shared.footer",
"instanceId": "contact_footer_1",
"props": {}
}"shared": "footer" is accepted shorthand for "$ref": "shared.footer". A typical page
is a flat sibling stack:
pages/home/components/
header.json # $ref shared.header, order 0
hero.tsx
product-rail.tsx
footer.json # $ref shared.footer, order 99What the build does
shared/components/ into a catalog.Expands each placement: clones the shared definition, deep-merges the placement's
props over the shared defaults, and keeps the placement's instanceId, title and
slot overrides.
Writes plain TemplateData — no $ref survives in dist/data.json. The renderer
and the merchant editor never see the indirection.
Because expansion happens at build time, editing shared/components/header.tsx once
updates every page on the next build — one place to change the design, and clean git
diffs. Each placement can still override props (a sticky header on home, a static one on
contact) while sharing the code.
Runtime references
After a component is published to the backend template_components library (your own
library or the marketplace), stores place it by id:
{ "type": "reference", "refId": "cmp_...", "refVersion": 3, "props": {}, "slots": {} }Lithium resolves these server-side on every render into full custom nodes — fix the
library component once and every store placement updates. Missing refs are skipped
fail-soft. The mechanics live in the
data model.
Kit $ref: "shared.*" and runtime type: "reference" look similar but are different
machines: $ref is a build-time copy that disappears from the compiled blob, while
reference is a live pointer resolved by the server at render time. Do not mix up
the two syntaxes.
Library placements are copies
A third thing looks like reuse but is not: entries in the theme's drag-drop catalog
(schema.library.json). When the merchant drops one
into a page the editor makes an inline copy — later edits to the library entry do
not propagate. Use the library for optional, one-off blocks; use shared/ for chrome
that must stay consistent.
Choosing a layer
| Situation | Use |
|---|---|
| Header/footer on every page of your theme | shared/components/ + $ref |
| Order-form field blocks reused across product and landing pages | shared/components/ + $ref |
| A block merchants may optionally add from the editor | Theme library (copy-on-drop) |
| One component maintained centrally across many stores | Publish to template_components, place with type: "reference" |
Pitfalls
| Bad | Good |
|---|---|
| Copy-pasting header JSX onto home + products | One shared/components/header.tsx + two $ref stubs |
shared/components/header/component.json + component.jsx for a leaf | Flat shared/components/header.tsx with export const meta |
| Deep hand-built shell trees when siblings work | Flat pages/<page>/components/ stack + $ref |
Duplicate instanceId across placements | Unique instanceId per placement |
| Expecting library drops to update later | Library inserts are copies — use shared/ or runtime references |
Related: template kit for the build workflow, schema library for the drag-drop catalog, and the order form for field blocks worth sharing.