Theme landing page templates¶
A theme can ship ready-made landing pages. While your theme is a shop's
active theme, each template appears as a card in the merchant dashboard's
Pages → Add page → Landing page picker, badged Theme: <your-slug>.
Picking one creates a new draft page of type landing — no header/footer,
seeded with the template's widgets — that the merchant can edit and publish
like any builder page.
This is separate from default page templates (theme/templates/*.json,
section-level system pages) and from global landing templates (authored
centrally by the platform's template shop). Theme landing templates travel
with the theme.
Where they live in the package¶
theme.zip
└── theme/
├── templates/ # default/system pages (product, category, …)
├── header-footer/ # header/footer presets
├── widgets/ # custom widget defs (x-<slug>-*)
└── landing-templates/ # ← landing page templates (this doc)
├── launch-offer.json
└── lookbook.json
In the theme kit, author them at src/themes/custom/landing-templates/*.json
— the build copies the folder into the package unchanged.
File format (widgets-v1)¶
{
"name": "Launch Offer",
"description": "Full-bleed hero with a product grid and closing CTA.",
"widgets": [
{
"id": "custom-html-hero",
"name": "custom-html",
"active": true,
"wType": "widgets",
"data": {
"html": "<section class=\"lp-hero\"><h1>Big launch</h1><p>Limited-time offer.</p></section>",
"css": ".lp-hero{padding:96px 16px;text-align:center;background:#111827;color:#fff}",
"settings": { "containerType": "full-width" }
}
},
{
"id": "filter-products-1",
"name": "filter-products",
"active": true,
"wType": "widgets",
"data": { "settings": { "containerType": "container", "productLimit": 8 } }
}
],
"pageSettings": { "showHeader": false, "showFooter": false }
}
| Field | Required | Meaning |
|---|---|---|
name |
yes | Picker card title, and the created page's name |
description |
no | Picker card subtitle |
widgets |
yes (array) | Page content in the dashboard builder's PageWidget[] format |
pageSettings |
no | Page-level settings; landing pages hide header/footer by default anyway |
Widget ids are re-minted when a merchant uses the template — placeholder ids
are fine.
What widgets can I use?¶
Any canonical widget the storefront renders (custom-html, slider,
banners, filter-products, product-slider, content-editor, button,
timeout, services, …) plus your theme's own custom widgets
(x-<slug>-*). Unknown widget names fail kit validation and would render as
hidden blocks on the storefront.
The custom-html widget is the primary landing building block:
data.html— raw HTML (no<script>execution)data.css— injected in a<style>tag with the section. Prefix your selectors (e.g..lp-hero …) so they don't affect the rest of the page.data.settings— standard block styling (containerType, padding, background,customCSS, per-device visibility)
Single-product order form (landing-checkout widget)¶
For a single-product landing page (product + on-page order form, common for
cash-on-delivery), use the landing-checkout widget ("Order Form
(Checkout)"). It renders the product card, variant/quantity pickers, the full
checkout form (customer details, shipping, COD/gateway, OTP) and submits the
order — all on the same page, no separate cart/checkout.
{
"id": "landing-checkout-1",
"name": "landing-checkout",
"active": true,
"wType": "widgets",
"data": {
"products": [{ "id": 25950, "slug": "test-product-05" }],
"settings": { "containerType": "container", "showAdditionalDetails": true }
}
}
data.products— product refs ({id, slug}) the form sells; one for a single product, several for a bundle. On a shipped template, prefer leaving this empty so the merchant picks their own product.settings.showAdditionalDetails— show the shop's extra checkout fields ("Additional details") after Shipping; self-hides when there are none.
If your theme ships its own checkout/checkout-design.tsx¶
The on-page order form reuses your theme's checkout design, so honor these
optional props (the kit's checkout/checkout-design.tsx already does — copy its
isLandingPage blocks when customizing):
isLandingPage— when true: hide the breadcrumb, hide the "Order Products" card, and render<CheckoutCustomFields />inline right after Shipping Details.showAdditionalDetails— gate that inline<CheckoutCustomFields />.onUpdateQuantity(skuId, qty)/onRemoveFromLandingCart(skuId)— wire the quantity −/+ and remove controls in the Order Summary items.
Ignoring the props is safe (the normal checkout is unchanged); the on-page form just won't get the landing-specific layout.
Authoring workflow¶
The fastest way to author a template:
- Build the landing page visually in the dashboard builder (Pages → Add page → Landing page → Blank landing).
- Mix
custom-htmlsections with product widgets as needed. - Copy the saved page's
widgetsarray into alanding-templates/<file>.jsonwith aname+description. npm run validatein the kit — checks JSON validity, required fields, and every widget name against the canonical contract.
Lifecycle¶
- Submit / re-submit: at materialize-seed time the platform parses
theme/landing-templates/*.jsoninto thetheme_landing_templatesregistry. Files must carryname+ awidgetsarray or the package is rejected. - Update: re-uploading the theme upserts templates by filename and deletes rows for files no longer in the package — same lifecycle as custom widget defs.
- Serve: the dashboard fetches
GET v2/purchase-theme/active/landing-templatesfor the shop's active theme; inactive themes' templates don't appear. - Use: creating from a template copies the widgets into a normal page —
pages already created keep working if the theme is later deactivated
(canonical widgets keep rendering; your
x-<slug>-*widgets follow the usual inactive-theme hide-and-warn policy).