.dev
Storefront templates

AI and templates

How AI is wired into the template system — component generation, page editing, landing pages, the validation and repair pipeline, and the kit's agent pack.

AI touches the template system at four points: generating custom components, editing whole pages, generating product landing pages, and AI-assisted authoring in the kit. This page maps those surfaces for people building tooling on top of them. General AI API concerns (auth, billing, model routing) live in the AI API docs.

All server endpoints below are authenticated RPC actions under /api/v1/actions/... (POST, except the GET job-status poll), permission-checked per store — component generation requires template write access (store_templates.write or template_components.write); the filter generator only needs read access.

EndpointProduces
generateCustomComponentCodeOne custom component: code + propsSchema + props (+ optional slots)
editTemplatePageUsingAiA full page structure (props + sections) for the editor
generateAiLandingPageAsync job → a complete product landing page
getAiLandingPageJobStatusJob polling (status, landingPageId)
generateProductLandingPageTemplateDataLanding-page TemplateData from an admin-curated landing template
generateListFilterUsingAiNatural language → flat filter or filterator JSON

Component generation

generateCustomComponentCode is the workhorse — it powers the "generate with AI" and "edit with AI" buttons on every custom block in the store editor.

Request essentials:

{
  "storeId": "…",
  "input": "A hero with a countdown and two CTAs",
  "mode": "create",
  "attachments": [
    { "type": "image", "value": "https://…", "label": "match this layout" },
    { "type": "url", "value": "https://example.com/inspiration" },
    { "type": "product", "value": "<product-id>", "prompt": "use its photos as style reference" }
  ],
  "styleContext": "…compact digest of neighboring components…"
}
  • attachments — a unified array of { type, value, label?, prompt? }: reference images (value is a URL, fetched and inlined into the prompt), web pages (HTML excerpt or image), and store products (value is the product id; its catalog images are pulled server-side). Labels and per-attachment prompts ride along.
  • styleContext — a compact JSON digest of the sibling components on the same page, so a generated block matches its neighbors' palette, spacing and tone instead of being designed in a vacuum.
  • Edit mode adds the current state: currentCode, currentPropsSchema, currentProps, and — critically — currentSlotsSchema / currentSlots / currentSlotsLayout. The prompt instructs the model to apply a minimal patch and to either echo slot fields unchanged or omit them entirely (omission means "keep what the merchant has"). Without that contract, unrelated edits used to wipe slot children.

Response envelope:

{
  "success": true,
  "code": "function App() { … }",
  "propsSchema": { "heading": { "type": "string", "name": "Heading" } },
  "props": { "heading": "…" },
  "slotsSchema": { "body": { "name": "Body", "maxChildren": 4 } },
  "slots": { "body": [ { "type": "custom", "title": "…", "code": "…" } ] },
  "slotsLayout": { "md": { "type": "row", "children": ["body"] } },
  "title": "Hero with countdown",
  "message": "OK"
}

The invariants match the data model: slot fields are top-level (never inside props), every slot child carries type and title, and slotsLayout leaves must be keys of slotsSchema. The service actively repairs the common model mistakes — lifting slots that were misplaced under props, backfilling missing slot arrays in create mode, dropping layout nodes that point at non-existent slot ids, and inferring type/title on bare slot children.

The validation and repair pipeline

Generated code is never trusted as-is. Each response runs a gauntlet before it reaches the client, and blocking failures are fed back to the model as corrective turns (up to two retries; token usage accumulates across attempts for billing):

Static validation (errors block, warnings advise) enforces the react-live sandbox contract from custom components:

  • Code parses as JSX; exactly one top-level function App() with no parameters and no other top-level function declarations.
  • No import / export / require() — the sandbox pre-injects all globals.
  • No bare hooks: useState(...) is rejected, React.useState(...) required.
  • Every .map(...) returning JSX must set key on the outermost element.
  • Unknown bare identifiers (not in the sandbox scope, never declared) warn — they would throw ReferenceError at render time.
  • props defaults without a matching propsSchema entry warn (the merchant could not edit them).

Security scan rejects eval, Function, require, process, globalThis, Buffer, dynamic import(), javascript: URLs, dangerouslySetInnerHTML, while (true) loops, and sources over 120k characters. The same scanner gates marketplace releases: any public or paid publish walks the entire TemplateData tree (pages, sections, children, slots) and scans every code string.

When retries are exhausted but the result still parses, the API returns it anyway with the unresolved issue codes in message — the editor preview surfaces the runtime error rather than the merchant losing the generation.

Page-level AI editing

editTemplatePageUsingAi runs the same idea one level up: instead of one component, the model plans a whole page.

  • Input: storeId, pageId, mode (create / edit), prompt and/or attachments, plus context — currentPage, templateSchemaSummary, templateDefaultsPage, templatePromptInstructions, and policy lists allowedSectionKeys / allowedComponentTypes.
  • The structure pass returns { page: { props, sections } } (optionally a globalPropsPatch). It is validated hard: sections must be non-empty arrays, section keys must be in allowedSectionKeys, and every component type in the tree must be in allowedComponentTypes.
  • In edit mode the model sees a compact outline of the current page — custom code is stripped and replaced with a hasCode marker so small models get a stable page skeleton instead of a multi-kilobyte payload.
  • Surgical merge: prompts that read like small deltas ("add", "insert", "keep", "only" — and nothing destructive like "rebuild" or "from scratch") trigger a merge of the AI page with the current page keyed by instanceId, so untouched blocks survive verbatim. "Add a scroll button to the hero" cannot wipe the hero.
  • Custom blocks in the planned structure arrive as placeholders — no code, just props._customComponentDescription (and _title). The server then materializes each placeholder by running component generation recursively (with an editor-style prompt so children match the parent stylistically), including placeholders nested inside slot trees.

Landing-page generation

generateAiLandingPage turns a short idea plus a product into a complete standalone landing page. It is asynchronous:

POST generateAiLandingPage with storeId, productId, optional name, and prompt and/or attachments. Returns a job id immediately.

Prompt enhancement — a first model call expands the idea ("Classic luxury theme") into a full design brief using the store and product context.

One-shot generation — a second call produces a design spec plus TemplateData defaults. The output is hard-validated to contain exactly one page: pages.landing_page — anything else is rejected.

Placeholder filling — empty slots get description placeholders which are materialized through the same component-generation pipeline (validation, security scan, retries included).

Poll GET getAiLandingPageJobStatus?jobId=… (the merchant app polls every ~10 s) until it returns a terminal status and the created landingPageId. A notification also fires.

A sibling endpoint, generateProductLandingPageTemplateData, customizes an admin-curated landing template (templateId + storeId + productId + prompt) instead of designing from scratch — same validation ideas, but the model fills a known-good structure.

Natural-language filters

generateListFilterUsingAi translates "bestsellers under 5000 still in stock" into either flat params or a filterator JSON tree, driven by a capability schema the client sends (so the model only uses fields the target list actually supports). The merchant app uses it for the orders and products list screens; it needs only read scopes.

How the store editor uses AI

The merchant app's template editor is the main consumer of these endpoints:

  • Per-block generate/editgenerateCustomComponentCode with mode, attachments and styleContext; edit mode sends the block's current fields so slots survive.
  • Page assistanteditTemplatePageUsingAi for "build me a home page like…" and surgical page tweaks.
  • Landing page wizardgenerateAiLandingPage + status polling.
  • The editor applies responses using the omission semantics above: absent slots* fields in an edit response mean "keep the current values".

AI-assisted authoring in the kit

The other half of the story is local: the template kit treats coding agents (Cursor, Claude Code, …) as first-class authors. Every feeef template init — and every marketplace unpack — ships an agent pack in the theme folder:

ShippedPurpose
AGENTS.mdThe authoring contract: mental model, hard rules, commands — the first thing an agent reads
docs/A curated subset of the canonical template docs (custom components, order form, filterator, PageSpeed, currency, design system, …) with a task index
.cursor/skills/feeef-theme and feeef-filterator skills — task-triggered playbooks
.cursor/rules/Always-on rules (design system, order form, PageSpeed, filterator, theme source)
design-system.mdStarter visual contract the theme must fill in — see conventions
scripts/check-perf.mjsThe static performance scanner
types/*.d.ts + tsconfig.jsonTeach tsserver the react-live globals (FeeefLivePropsOf, scope hooks) so agents get real type feedback

The pack is copied fill-missing-only — re-running init or unpacking a purchased theme never overwrites your customizations, but does backfill docs added since the theme was scaffolded. The net effect: pointing a coding agent at a theme folder gives it the same contracts the server-side generation pipeline enforces, so locally-authored and AI-generated components obey one set of rules.

Building on top

If you are wiring these endpoints into your own tooling:

  • Treat the response envelope as authoritative — apply code, propsSchema, props together, keep slot fields top-level, and honor omission-means-keep in edit flows.
  • Reproduce the client-side contract from custom components when rendering previews; anything that passes the server validator runs in the sandbox.
  • Send styleContext whenever you generate into an existing page — it is the difference between a matching block and a jarring one.
  • For auth, model selection and billing details, see the AI API.

On this page