Declare SEO on the route. PageGraph derives the rest.
Head tags, sitemap, robots.txt, JSON-LD and a CI gate for TanStack Start, from one declaration per route. This site is built with it.
Frontmatter of /docs/quick-start
title: "Quick start", related: ["/docs/guides/ci-check", "/docs/concepts/the-graph", "/docs/reference/configuration"], description: "Add PageGraph to an existing TanStack Start app in five steps and finish by running pagegraph check against your own routes.",
Derived at build
- Title
- Quick start | PageGraph docs
- Canonical
- https://pagegraph.arya.sh/docs/quick-start
- Breadcrumb
- Home › Docs › Quick start
- Markdown
- /docs/quick-start.md
One object on the route. Every output reads it.
staticData.seo holds the crawl policy and the head together. The rendered head, the graph and the checks read the same object, so the page and the sitemap cannot disagree.
export const Route = createFileRoute("/(docs)/docs/quick-start")({
staticData: {
seo: {
kind: "page",
crumb: "Quick start",
sitemap: { priority: 0.9, changeFrequency: "monthly" },
related: ["/docs/guides/ci-check", "/docs/reference/cli"],
modifiedAt: "2026-11-13",
head: {
title: "Quick start",
description: "Add route-declared SEO to a TanStack Start app.",
},
},
markdown: "rendered",
},
head: seo.head,
});
- A breadcrumb
crumbbecomes a BreadcrumbList: Home › Docs › Quick start. - A sitemap entry
sitemapandmodifiedAtbecome the page's<url>with its<lastmod>. - Typed links
A renamed
relatedtarget is a type error. A target with no link card fails the check. - The head
seo.headrenders the title, description, canonical, Open Graph tags and JSON-LD. - A Markdown twin for agents
The page is captured as
/docs/quick-start.mdand listed in/llms.txt.
Drift fails the build, not the crawl.
pagegraph check reads the same graph through pagegraph.config.ts. Broken declarations exit 1 in CI, before a crawler finds them.
$ pagegraph check Structural (2): ✗ [dead-edge] /blog/launch related edge from "/blog/launch" points at "/pricing", which is not a node in the graph. ✗ [robots-blocks-sitemap-url] /solutions Disallow: /s blocks a sitemap page. 2 structural, 0 editorial $ pagegraph check ✓ No violations. The SEO graph is clean.
- Structural exit 1Dead or duplicate edges, path collisions, a redirect or a noindex page in the sitemap, a published URL that robots.txt blocks.
- Editorial reportedDuplicate titles and descriptions, descriptions too long for results, and pages older than your freshness policy.
- RenderedWith
--site, it fetches every declared page and checks the HTML a crawler receives: one H1, the canonical, robots meta, visible FAQ answers and prices.

Rename one route. Every copy has to follow.
A silent, 60-second film of the problem and the fix, drawn from the repository's test fixture.
Each entry runs in one place.
Runtime entries have no dependencies beyond their listed peers. Build entries resolve to a stub under workerd, so a Worker bundle that reaches one fails at import instead of on its first request.
| Entry | Runs in | For |
|---|---|---|
| Runtime | ||
| pagegraph | Worker, SSR, browser | Build the graph, render sitemap and robots, run checks |
| pagegraph/react | Worker, SSR, browser | seo.head, breadcrumbs, JSON-LD |
| pagegraph/tanstack-start/server | Start server routes | robotsTxt, sitemapXml, llmsTxt |
| Build | ||
| pagegraph/tanstack-start | vite.config.ts | The Vite plugin that builds the graph |
| pagegraph/config | pagegraph.config.ts | CLI policy and the graph loader |
| pagegraph | CLI, on Bun | check, graph, audit, links verify |
Five steps to a checked graph.
The tutorial adds PageGraph to an existing TanStack Start app.
- Bind your siteOrigin, name and organization, once. Route files never see them.
lib/seo.ts - Declare each pageCrawl policy and head together, on the route.
staticData.seo - Add the pluginIt builds the graph inside your Vite pipeline.
vite.config.ts - Serve robots and sitemapOne line each, as server routes.
robots[.]txt.ts - Gate CIThe CLI reads the same graph.
pagegraph check
Read this site's own output.
Each file comes from the declarations in site/src/routes. CI runs pagegraph check on every push.