AEO & GEO

How the storefront makes itself legible to AI answer engines and generative search, with built-in content negotiation, structured data, and discovery surfaces.

AEO & GEO

Answer Engine Optimization (AEO) and Generative Engine Optimization (GEO) are the practices of making a site legible to the AI surfaces that increasingly mediate commerce — ChatGPT, Claude, Perplexity, Google AI Overviews, and the long tail of agent-driven shopping. Where classical SEO optimizes for crawlers that build a search index, AEO/GEO optimizes for models that read a page once and synthesize an answer. The legibility bar is higher: noisy markup, hydration-only data, and content trapped behind interaction all degrade an LLM's ability to recover the underlying facts.

The template ships with several built-in surfaces that contribute to AEO/GEO. The largest of them is content negotiation — serving structured markdown to clients that ask for it — but the supporting cast (JSON-LD schema, sitemaps, OpenGraph metadata) is what makes a page consistently parseable across surfaces.

Surfaces in the template

SurfaceWhat it doesKey files
Content negotiationServes clean markdown to AI clients via Accept: text/markdownnext.config.ts, app/md/**, lib/markdown/**
Schema.org JSON-LDEmbeds structured Product, BreadcrumbList, and Organization datacomponents/product-detail/schema.tsx, components/schema/**
SitemapEnumerates products, collections, and pages for crawlersapp/sitemap.ts, lib/shopify/operations/sitemap.ts
RobotsDeclares crawl policy, including allowances for known AI user agentsapp/robots.ts
OpenGraph & Twitter metadataProvides title, description, and image previews per routelib/seo.ts, route-level generateMetadata

The rest of this page goes deep on content negotiation. The other surfaces are documented inline in the linked files.

Content Negotiation

Product, collection, and search pages serve structured markdown when clients send Accept: text/markdown. Browsers continue to receive HTML; AI agents and crawlers get clean, parseable markdown from the same URL.

This is built in and requires no configuration. Every markdown route lives under a single top-level app/md/ directory so all content-negotiation handlers are co-located and easy to find.

How it works

GET /products/speaker        (Accept: text/markdown)
GET /collections/speakers    (Accept: text/markdown)
GET /search?q=speaker        (Accept: text/markdown)
    ↓
next.config.ts rewrite
    ↓
/md/products/[handle]
/md/collections/[handle]
/md/search
    ↓
Route handler response: text/markdown

Browsers that don't send Accept: text/markdown are unaffected — the rewrite only fires when the header matches.

Testing

bash
# Returns structured markdown
curl -H "Accept: text/markdown" http://localhost:3000/products/speaker

# Returns collection markdown with products, filters, and pagination
curl -H "Accept: text/markdown" http://localhost:3000/collections/speakers

# Returns search markdown with query, filters, and result summaries
curl -H "Accept: text/markdown" "http://localhost:3000/search?q=speaker&sort=price-low-to-high"

# Returns the normal HTML page
curl http://localhost:3000/products/speaker

The Vary: Accept header ensures CDNs cache markdown and HTML responses separately.

What's included in the markdown

  • Product pages — handle, brand, category, pricing, options, variants, specs, images, tags, and SEO metadata
  • Collection pages — collection metadata, description, applied filters, available filters, products, pagination, image, and SEO metadata
  • Search pages — query metadata, active collection filter, applied filters, available filters, products, and pagination state

Key files

FilePurpose
next.config.tsRewrite rules matching Accept: text/markdown
app/md/products/[handle]/route.tsProduct markdown route handler
app/md/collections/[handle]/route.tsCollection markdown route handler
app/md/search/route.tsSearch markdown route handler
lib/markdown/product.tsproductToMarkdown() converter
lib/markdown/collection.tscollectionToMarkdown() converter
lib/markdown/search.tssearchResultsToMarkdown() converter
lib/markdown/catalog.tsShared product/filter/pagination formatting helpers
lib/markdown/utils.tsFormatting helpers (prices, tables, escaping)

Multi-locale

If you have enabled Shopify Markets, the rewrite fires before locale routing — no additional configuration is needed. Pass a ?locale= query parameter to the markdown endpoint for locale-specific pricing and collection/search context.

Chat

Tip: You can open and close chat with I

0 / 1000