Skip to main content
A React/Vite storefront normally has no application server at request time. Use @commercengine/seo in the browser for page head data, generate the discovery assets before the Vite build, and register @commercengine/ai from app bootstrap or a root effect.

1. Share site and route configuration

src/lib/commerce-seo.config.ts

2. Create the SEO instance

A browser-only SPA can use the canonical Storefront factory directly:
src/lib/seo.ts
Build head data asynchronously because route resolution and enrichment can be async:
src/lib/use-commerce-seo.tsx

3. Render the head model

React 19 hoists <title>, <meta>, and <link> into <head>. JSON-LD can remain inline in the page tree.
src/components/Seo.tsx
React does not deduplicate these tags against static metadata in index.html. Remove generic product/page Open Graph tags or mark and retire them when page-specific head data exists. Otherwise crawlers and social platforms can read the first, generic tag instead of the product tag.
The Commerce Engine starters demonstrate a safe data-seo-default pattern that removes only overridden defaults and restores them on unmount.

4. Generate SEO assets before Vite builds

A SPA cannot answer Accept: text/markdown or generate robots.txt on demand. Write physical files into public/ before vite build. Run the prebuild as TypeScript so it can import the exact same pure configuration used by the app and AI tools. Add a TypeScript executor and a small env loader:
scripts/generate-seo-assets.ts
package.json
Do not hard-code indexable: true in this shared build script. With the option omitted, Vercel, Netlify, Cloudflare, and generic Node production/preview signals remain authoritative. A local build with no reliable deployment signal is intentionally generated as non-indexable. Generated files are build artifacts. Ignore them instead of committing them:
If the app is deployed behind a static host, configure deep-link fallback to index.html. Static files such as .md, robots.txt, and sitemap.xml are normally matched before that rewrite.

5. Register AI tools

Registration does not need to wait for the session. Mount it from app startup or a root effect and abort it on teardown.
src/lib/register-agent-tools.ts
The browser can remain fully functional without WebMCP. registerCommerceWebMcp() returns null when the capability is unavailable. The root effect/bootstrap that calls registerAgentTools() should keep the returned controller and call .abort() during cleanup so a late registration cannot survive unmount.

Production checklist

  • build outputs /robots.txt, /sitemap.xml, /llms.txt, /sitemap.md, and product/category .md files
  • product pages render one product schema, not a package schema plus a hand-written duplicate
  • static Open Graph defaults do not override page-specific metadata
  • deep product/category URLs work on a hard reload
  • development console shows a Commerce AI registration diagnostic
  • AI and SEO both import the same site and routes configuration

Reference starter

See the Soja React/Vite storefront for the complete production pattern.