---
title: AEO & GEO
description: How the storefront makes itself legible to AI answer engines and generative search, with built-in content negotiation, structured data, and discovery surfaces.
type: guide
---

# AEO & GEO



# 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

| Surface                          | What it does                                                                                           | Key files                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| **Content negotiation**          | Serves clean markdown to AI clients via `Accept: text/markdown`                                        | `next.config.ts`, `app/md/**`, `lib/markdown/**`                                         |
| **Schema.org JSON-LD**           | Embeds structured `Product`, `BreadcrumbList`, and `Organization` data                                 | `components/product-detail/schema.tsx`, `components/schema/**`                           |
| **Sitemap**                      | [Sitemap index + paged children](/docs/anatomy/sitemap) for products and collections                   | `app/sitemap.xml/route.ts`, `app/sitemap/[shard]/route.ts`                               |
| **Robots**                       | Declares crawl policy and blocks faceted (sort/filter) collection URLs                                 | `app/robots.ts`                                                                          |
| **`llms.txt`**                   | Curated `/llms.txt` index of collections and discovery links for AI agents                             | `app/llms.txt/route.ts`, `lib/markdown/llms.ts`                                          |
| **OpenGraph & Twitter metadata** | Per-route title/description/image previews, plus `product` OG tags (type, price, availability) on PDPs | `lib/seo.ts`, `components/product-detail/open-graph.tsx`, 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

| File                                   | Purpose                                             |
| -------------------------------------- | --------------------------------------------------- |
| `next.config.ts`                       | Rewrite rules matching `Accept: text/markdown`      |
| `app/md/products/[handle]/route.ts`    | Product markdown route handler                      |
| `app/md/collections/[handle]/route.ts` | Collection markdown route handler                   |
| `app/md/search/route.ts`               | Search markdown route handler                       |
| `lib/markdown/product.ts`              | `productToMarkdown()` converter                     |
| `lib/markdown/collection.ts`           | `collectionToMarkdown()` converter                  |
| `lib/markdown/search.ts`               | `searchResultsToMarkdown()` converter               |
| `lib/markdown/catalog.ts`              | Shared product/filter/pagination formatting helpers |
| `lib/markdown/utils.ts`                | Formatting 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.

## llms.txt

`/llms.txt` is a curated, machine-readable index of the storefront — an [emerging convention](https://llmstxt.org) that is to AI agents what `robots.txt` is to crawlers. It links to the search entry point, the collection catalog, and the sitemap/robots discovery surfaces, and tells agents that those pages also serve clean Markdown under `Accept: text/markdown` (the content negotiation above).

The route is dynamic: collections are pulled live from Shopify and capped so the file stays a concise index rather than an exhaustive dump — the full URL set lives in the [sitemap](/docs/anatomy/sitemap). Because the links honor content negotiation, the virtual `/collections/all` catalog page is served as Markdown too, not just HTML.

### Key files

| File                    | Purpose                                                      |
| ----------------------- | ------------------------------------------------------------ |
| `app/llms.txt/route.ts` | Route handler; fetches collections and serves `text/plain`   |
| `lib/markdown/llms.ts`  | `llmsTxt()` generator that builds the index from the catalog |


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)