# Declare SEO on the route. PageGraph derives the rest.

URL: https://pagegraph.arya.sh/

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.

[Start the tutorial](https://pagegraph.arya.sh/docs/quick-start)

Install with: bun add pagegraph

This site's own route graph: 20 pages, 17 breadcrumb edges and 40 related links, read from the build.

## 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.

An example route declaration for /docs/quick-start, with crumb, sitemap, related, head and markdown fields.

## What one declaration derives

1. **A breadcrumb** `crumb` becomes a BreadcrumbList: Home › Docs › Quick start.
2. **A sitemap entry** `sitemap` and `modifiedAt` become the page's `<url>` with its `<lastmod>`.
3. **Typed links** A renamed `related` target is a type error. A target with no link card fails the check.
4. **The head** `seo.head` renders the title, description, canonical, Open Graph tags and JSON-LD.
5. **A Markdown twin for agents** The page is captured as `/docs/quick-start.md` and 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.

Example pagegraph check output: two structural violations, then a clean run.

## What the check reports

- **Structural**: Dead or duplicate edges, path collisions, a redirect or a noindex page in the sitemap, a published URL that robots.txt blocks.
- **Editorial**: Duplicate titles and descriptions, descriptions too long for results, and pages older than your freshness policy.
- **Rendered**: With `--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.

A table of six of PageGraph's entry points and where each runs; the full list is on /docs/reference/entries.

[See all 13 entries](https://pagegraph.arya.sh/docs/reference/entries)

## Five steps to a checked graph.

The tutorial adds PageGraph to an existing TanStack Start app.

[Start the tutorial](https://pagegraph.arya.sh/docs/quick-start)

1. **Bind your site** Origin, name and organization, once. Route files never see them.
2. **Declare each page** Crawl policy and head together, on the route.
3. **Add the plugin** It builds the graph inside your Vite pipeline.
4. **Serve robots and sitemap** One line each, as server routes.
5. **Gate CI** The CLI reads the same graph.

## Read this site's own output.

Each file comes from the declarations in `site/src/routes`. CI runs `pagegraph check` on every push.
