SEO & AEO
Help search engines and AI crawlers find, understand, and rank your site.
Overview
Search engine optimization (SEO) and answer engine optimization (AEO, for AI crawlers) both rely on the same fundamentals. Pages need clear titles, descriptions, preview images, and machine-readable hints about what each page contains.
When you render your site into an MPA with npx mnfst-render (see websites), Manifest generates all of this automatically. No configuration is needed for most sites. This article covers what gets generated, when you'd want to customize it, and how.
Automation
The rendering process automatically populates:
- Page titles and descriptions for browser tabs and search results
- OpenGraph and Twitter Card tags for previews when your pages are shared on social media
- OG image snapshots, automatic 1200×630 PNG screenshots of each page, used as the social preview image
- Structured data in JSON-LD format, so search engines understand the content type (article, breadcrumb, product, etc.) and can render rich results
- sitemap.xml listing every page so crawlers can find them
- robots.txt telling crawlers what they can and can't index
- llms.txt and llms-full.txt for AI crawlers, per the llmstxt.org convention
Defaults are derived from each page's rendered content. The first <h1> becomes the title, the first <p> becomes the description, a screenshot of the page becomes the social-share image. For most sites that's enough.
llms.txt
The renderer writes llms.txt (a curated index of your site) and llms-full.txt (the concatenated full content) per the llmstxt.org convention. AI crawlers prefer reading this structured plaintext over scraping rendered HTML. Pages are grouped into sections by their first URL segment.
Sitemap Freshness
sitemap.xml lists every rendered route. Each entry includes a <lastmod> timestamp telling crawlers when the page last changed, so they know when to re-check it.
The renderer computes <lastmod> from the most recent modification time across every file that contributes content to the route. This includes local csv, json, yaml, and md files, but excludes remote sources like Appwrite or HTTP endpoints, since there's no local modification time to reference.
Customization
The most common reasons to override the auto-generated values are:
- A designed social-share image for important pages, instead of a page screenshot
- Per-page titles and descriptions sourced from your CMS or data files
- A branded title pattern across the whole site (e.g. every page reads
Page Name — Site Name) - Article structured data with author, publish date, and similar metadata
Manifest gives you four ways to set these values, applied in priority order. If a value is already set by a higher-priority source, the lower-priority ones are skipped for that value.
1. Per-route head tags in a component. The most direct override, useful for one-off pages, placed directly in their HTML:
component.html <template data-head>
<title>About Us — Acme</title>
<meta name="description" content="Founded in 1985, Acme builds…">
<meta property="og:image" content="/assets/og/about.png">
</template>
2. Global head tags in index.html's <head>, applied as a fallback to every page (e.g. a default social-share image used for any page that doesn't supply its own).
3. Per-route expressions in manifest.json's render.meta. Useful for generating titles and descriptions from a data source across many routes at once:
manifest.json {
"render": {
"meta": {
"title": "$x.docs.$route('path').name + ' — Acme'",
"description": "$x.docs.$route('path').description",
"image": "$x.docs.$route('path').image"
}
}
}
4. Static fallbacks in manifest.json's render.meta.fallback. Used when the expression above returns nothing:
manifest.json {
"render": {
"meta": {
"fallback": {
"title": "Acme",
"description": "We build things since 1985.",
"image": "/assets/og/default.png"
}
}
}
}
If none of these set a value, Manifest falls back to the smart defaults derived from the page content. If even those come up empty, your manifest.json's PWA fields (name, description, icons) act as a last-resort.
Structured Data
Structured data tells search engines what type of content each page contains. Google uses it to render rich results, like article cards, breadcrumb trails, FAQ accordions, and product cards. Manifest can inject three common types automatically:
manifest.json {
"render": {
"structuredData": {
"WebSite": true,
"Article": true,
"BreadcrumbList": true
}
}
}
Pass true to auto-fill from page content, an object to specify exact field values, or false to suppress that type.
Reference
The full set of options under render.meta in manifest.json, plus the render.structuredData row covered above:
manifest.json {
"render": {
"meta": {
"title": "$x.docs.$route('path').name + ' — ' + $x.site.name",
"description": "$x.docs.$route('path').description",
"image": "$x.docs.$route('path').image",
"ogType": "'article'",
"imageSnapshots": true,
"defaults": true,
"fallback": {
"title": "Acme",
"description": "We build things since 1985.",
"image": "/assets/og/default.png"
}
}
}
}
| Option | Type | Default | Description |
|---|---|---|---|
meta.title / meta.description / meta.image / meta.ogType / meta.author |
Alpine expression | — | Per-route values evaluated in the live page context. Use $x.* to access any data source. Strings are treated as JS expressions, so wrap literals in quotes (e.g. "'article'"). |
meta.fallback.* |
string | — | Static fallback used when the corresponding expression returns null or empty. |
meta.imageSnapshots |
boolean | true |
Auto-snapshot each page (1200×630 PNG saved under /og/) and use as og:image. Set false to skip snapshots and rely on existing image sources only. |
meta.defaults |
boolean | true |
Smart defaults derived from the rendered page (h1, first p, etc.). Set false for fully explicit control. |
structuredData.<Type> |
object | true | false |
— | Inject JSON-LD <script> blocks. Pass true for auto-fill from page content (WebSite, Article, BreadcrumbList), an object for explicit field values, or false to suppress. |
Article does not exist
There is no documentation at this path.