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 full accepted list is schemas/canonical-widgets.json in the preset kit —
that file is what npm run validate checks against, so treat it as the
authority rather than this prose.
Content/landing widgets worth knowing before you hand-build the same thing in
custom-html: hero, cta-band, announcement-bar, testimonials, faq,
stats, logo-strip, feature-grid, pricing, bullet-list.
announcement-bar¶
A slim full-width strip for a short promo or notice — the canonical version of the announcement bars themes used to hardcode above the header. Put it first in a template's widget list so it sits at the top of the page.
{
"name": "announcement-bar",
"data": {
"text": "Free delivery on orders over ৳2,000",
"link": { "url": "/collections/sale", "urlType": "custom" },
"linkLabel": "Shop now",
"settings": {
"background": "#111827",
"textColor": "#ffffff",
"align": "center",
"size": "normal",
"sticky": false,
"dismissible": false
}
}
}
- A
linkwith an emptylinkLabelmakes the whole bar clickable. dismissibleis remembered per shopper and keyed by the message text, so changing the message brings the bar back for people who dismissed the old one.sizeiscompact|normal|tall.- It deliberately takes no
containerTypeor padding settings — an announcement bar is edge-to-edge by definition, so the standard block layout/background fields are not offered.
Don't ship your own top bar as a custom widget unless you need something the canonical one cannot express: a theme-scoped copy means merchants lose their message and colors the moment they switch themes.
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).