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:

  1. Build the landing page visually in the dashboard builder (Pages → Add page → Landing page → Blank landing).
  2. Mix custom-html sections with product widgets as needed.
  3. Copy the saved page's widgets array into a landing-templates/<file>.json with a name + description.
  4. npm run validate in 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/*.json into the theme_landing_templates registry. Files must carry name + a widgets array 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-templates for 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).