Skip to content

Astro docs site

The executable-stories-astro integration turns your test output into a full Starlight site — generated scenarios and your hand-authored docs side by side, with sidebar navigation, status badges, Mermaid diagrams, and search. It is live: a content loader watches your run JSON, so a fresh test run hot-reloads the open page. Nothing is written to disk — your tests stay the source of truth.

This guide scaffolds a new site. Already have an Astro site? See Add to an existing Astro site. Collating runs from many repositories? See the multi-repo docs hub guide.

Terminal window
npx --package executable-stories-formatters executable-stories init-astro my-docs
cd my-docs && pnpm install

This scaffolds a thin, ready-to-run Starlight project. The docs framework itself ships in the executable-stories-astro package, so the scaffold is just ~8 user-owned files — chiefly one config file you edit.

Then emit the run JSON your reporter must write (see below), run your tests in watch mode in one terminal, and astro dev in another:

Terminal window
pnpm dev # http://localhost:4321 — /stories, /explorer, and your docs

Editing a test re-runs it and the Stories pages update with no reload.

Everything lives in executable-stories.config.mjs, imported by both astro.config.mjs and src/content.config.ts:

import { defineExecutableStories } from 'executable-stories-astro';
export default defineExecutableStories({
source: '../reports/raw-run.json', // or `sources: [...]` for several suites
include: { tags: ['security'] }, // which scenarios to show (optional)
groupBy: 'tag', // feature | tag | source | status | none
docs: [{ path: 'src/content/docs/runbooks', label: 'Runbooks', base: 'runbooks' }],
// historyFile: '../reports/history.json', // CLI --history-file store for journey trends
theme: { preset: 'terminal', tokens: { pass: '#16a34a' } },
});
Field What it does
source / sources One run JSON, or several named suites (combined in one site, groupable by suite).
include / exclude Select scenarios by tags, status, or features.
groupBy How the index/Explorer categorise scenarios.
docs Authored markdown folders to surface in the nav.
views Persona views: filtered, re-grouped indexes at their own URLs (e.g. /for/product). See Tagging for your audience.
journeysBase / injectJourneys Where the journey walkthroughs mount (default /journeys; derived from journey:<id>:<n> tags).
statesBase / injectStates Where the UI-state catalog mounts (default /states; derived from state:<name> tags, viewport variants side by side).
driftBase / injectDrift Multi-source status comparison (default /drift; injected automatically with at least two sources).
historyFile Store maintained by CLI --history-file; enables recent-run stability on journey pages.
collection Collection name the loader feeds (default stories).
routeBase / explorerBase Where the pages mount (default /stories, /explorer).
agentEndpoints Inject /llms.txt and per-story Markdown twins at <routeBase>/<slug>.md (default true).
theme preset (default/terminal/minimal/vibrant), accent shorthand, and per-token tokens overrides. Restyles the story content; the Starlight shell keeps its own theme.

See the full reference in the executable-stories-astro README.

  • /stories — an index of every scenario, categorised by groupBy, each linking to a detail page with its Given/When/Then steps and docs. Styled out of the box; no CSS to wire.
  • /explorer — a searchable, filterable Scenario Explorer (by text, status, and tag).
  • /journeys — ordered multi-scenario walkthroughs derived from journey:<id>:<n> tags, each rendered as full scenario cards (storyboards included) under one aggregate status. Embed one in MDX with <StoryJourney id="..." />.
  • /states — a thumbnail grid of the states the product verifiably has, from state:<name> tags; viewport:* variants render side by side. Non-UI scenarios appear with data-card thumbnails from their story.state() snapshots.
  • /drift — with two or more sources, compares each scenario’s status side by side and floats disagreements or missing scenarios first.
  • Auto-built nav — spread storiesSidebar(config) into your Starlight sidebar and the Stories/Explorer links and your docs groups appear without hand-wiring. The nav stays fresh in dev: when a test run adds, renames, or removes scenarios, the integration triggers a dev-server restart so the sidebar rebuilds (status-only changes hot-reload without a restart).
  • Live trajectory — the shipped <Trajectory /> component shows “passed N → M since you started” across a watch session.
  • Agent endpoints/llms.txt indexes every scenario, and each story page has a plain-Markdown twin at /stories/<slug>.md, so the deployed site is consumable by agents and curl, not just browsers. Disable with agentEndpoints: false.
  • Design contextstory.link() entries pointing at Figma, Zeplin, Sketch, or Abstract (or deliberately labelled Design ...) appear on story and journey pages. The same link remains in the scenario’s normal docs.

If the CLI persists history, reuse that store in the site:

Terminal window
executable-stories format reports/raw-run.json --format html \
--history-file reports/history.json
export default defineExecutableStories({
source: '../reports/raw-run.json',
historyFile: '../reports/history.json',
});

Journey history is aggregated by run: any failed member fails the journey run. The badge uses the same stable/unstable/flaky classification as the HTML report.

views mounts audience lenses over the same scenarios — /for/product, /for/design, /for/support — each a filtered, re-grouped index driven by the tags your tests already carry:

views: [
{ base: '/for/product', include: { tags: ['audience:stakeholder'] }, groupBy: 'tag' },
{ base: '/for/design', include: { tags: ['storyboard'] } },
],

Each view appears in the sidebar under “Audiences” and renders the same interactive index as /stories, filtered to its audience. The tag vocabulary and per-persona recipes live in Tagging for your audience.

Authored MDX pages can pull scenarios in as live evidence, rendered from the same collection as the story pages — so an embed can never drift from the latest run:

import StoryScenario from 'executable-stories-astro/components/StoryScenario.astro';
import StoryStatus from 'executable-stories-astro/components/StoryStatus.astro';
We cap discounts at 30% — enforced end-to-end
(currently <StoryStatus id="checkout--caps-the-discount-at-30-percent" />):
<StoryScenario id="checkout--caps-the-discount-at-30-percent" />

<StoryScenario/> renders the full scenario card (steps, status, failure output, attached docs); <StoryStatus/> is an inline linked status pill. Both accept the stable scenario id (copy it from the Explorer), the URL slug, or the exact title, and render a visible callout when the id no longer matches — an embed never silently disappears. This pairs with <VerifiedBy/> (frontmatter verifiedBy: refs → a live pass/fail badge) for page-level verification.

Hand-authored docs live under src/content/docs. The scaffold loads them with authoredDocsLoader, a drop-in for Starlight’s docsLoader() that makes plain, GitHub-style markdown work without edits:

  • Auto-title from each file’s first # H1 (so frontmatter-free files import cleanly — the one field Starlight requires).
  • Cross-link rewriting so relative ./other.md links resolve to routes instead of 404ing.

Point a docs source’s path at a folder outside the site and set base to mount an external docs folder (e.g. another package’s docs/) under a URL prefix.

The loader reads the raw run JSON, which your reporter writes only when you set rawRunPath:

new StoryReporter({ formats: ['html'], rawRunPath: 'reports/raw-run.json' })

Point the config’s source at that path.

The scaffolded site is a standard Astro project — build it and deploy dist/:

Terminal window
pnpm build

Run your tests and regenerate the run JSON in CI before astro build so the deployed site reflects the latest results. Works with Vercel, Netlify, GitHub Pages, Cloudflare Pages, or any static host.

Terminal window
npx --package executable-stories-formatters executable-stories init-astro [directory]
Option Default Description
directory story-docs Where to create the site
--force false Overwrite if the directory exists
--update false Merge any new template deps (the framework updates via pnpm update executable-stories-astro)

Migrating from build-docs? It generated Markdown into src/content/docs/stories/ and has been removed in favour of the live integration, which renders stories from the run JSON with no generation step. Scaffold with init-astro and run astro dev. (format --format astro-markdown still exists for a one-off single-page Markdown export.)