.dev
Storefront templates

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:

LayerMechanismResolvedUse for
Kit shared libraryshared/components/ + $ref placementsAt template build — inlined into data.jsonChrome inside your theme: header, footer, order-form fields
Runtime referencestype: "reference" + refId nodesServer-side on every renderComponents 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 --> resolve

Kit 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

KindPath
Leaf custom (preferred)shared/components/<id>.tsxexport const meta + function App()
Leaf built-in stubshared/components/<id>.json
With slots / childrenshared/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 99

What the build does

Loads every entry under 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 TemplateDatano $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

SituationUse
Header/footer on every page of your themeshared/components/ + $ref
Order-form field blocks reused across product and landing pagesshared/components/ + $ref
A block merchants may optionally add from the editorTheme library (copy-on-drop)
One component maintained centrally across many storesPublish to template_components, place with type: "reference"

Pitfalls

BadGood
Copy-pasting header JSX onto home + productsOne shared/components/header.tsx + two $ref stubs
shared/components/header/component.json + component.jsx for a leafFlat shared/components/header.tsx with export const meta
Deep hand-built shell trees when siblings workFlat pages/<page>/components/ stack + $ref
Duplicate instanceId across placementsUnique instanceId per placement
Expecting library drops to update laterLibrary 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.

On this page