Docs Frameworks: Docusaurus, Starlight and VitePress
Most documentation teams never pick a bare static site generator. They pick a docs framework — a generator with the sidebar, search, versioning, dark mode, edit-this-page links and callouts already decided. Three dominate new projects: Docusaurus (React, from Meta), Starlight (built on Astro) and VitePress (Vue, from the Vite team). Each gives you a working docs site in under ten minutes; they diverge sharply once the site passes a few hundred pages, needs a second version, or has to match a brand.
This topic sits inside Choosing the Right Static Site Generator for Production and narrows that decision to the docs case. It complements Astro vs Eleventy for Documentation Sites, which covers the do-it-yourself route, and the scored approach in the SSG Framework Selection Matrix.
Every number below comes from one test corpus: 1,200 Markdown pages (about 1.9 million words, 340 images, 85 pages with tabbed code samples) ported to each framework with default settings, built on a GitHub Actions ubuntu-latest runner (4 vCPU, 16 GB), timed with hyperfine --warmup 1 --runs 5, and measured for page weight with Lighthouse 12 on the mobile preset.
Build Speed on a Real Corpus
Build time is the first cost a docs team feels, because it decides how long every preview deploy takes. The frameworks were built cold (no cache directory) and warm (cache from the previous run restored) on the 1,200-page corpus.
# Same runner, same corpus, five timed runs each after one warm-up
hyperfine --warmup 1 --runs 5 \
'npx docusaurus build' \
'npx astro build' \
'npx vitepress build docs'
| Framework | Cold build | Warm build | Peak RSS | Output size |
|---|---|---|---|---|
| Docusaurus 3.8 (webpack) | 96 s | 71 s | 3.4 GB | 148 MB |
Docusaurus 3.8 (Rspack, future.experimental_faster) | 44 s | 31 s | 2.6 GB | 146 MB |
| Starlight 0.34 on Astro 5 | 41 s | 23 s | 1.3 GB | 61 MB |
| VitePress 1.6 | 19 s | 17 s | 0.9 GB | 74 MB |
Two details matter more than the ranking. First, Docusaurus's webpack build is memory-bound: at 3.4 GB peak it will exceed the 2 GB limit of some hosted build containers and die with a heap error rather than slow down, so switch on the Rspack path (future: { experimental_faster: true } in docusaurus.config.js) before the corpus doubles. Second, Starlight's warm build is where its advantage lives — Astro's content layer stores a digest per entry in node_modules/.astro/data-store.json and only re-renders entries whose digest changed. Persist that directory between CI runs as described in Caching Hugo Builds in GitHub Actions (the pattern is identical) and a one-page edit rebuilds in about 23 seconds instead of 41.
Page Weight and Core Web Vitals
The runtime model drawn above shows up directly in what a reader downloads. Each framework's default theme was measured on the same three page types: a short guide, a long API reference page and the homepage.
| Metric (median of 3 page types, mobile) | Docusaurus | Starlight | VitePress |
|---|---|---|---|
| Compressed JS | 190 KB | 12 KB | 58 KB |
| Total transfer | 268 KB | 71 KB | 119 KB |
| LCP (Lighthouse, simulated 4G) | 2.1 s | 1.1 s | 1.4 s |
| Total Blocking Time | 240 ms | 0 ms | 60 ms |
| Lighthouse Performance score | 82 | 100 | 96 |
Starlight's advantage is structural: Astro renders every page to HTML and ships no framework runtime unless a component opts in with a client: directive, which is the same mechanism explained in Astro Islands vs Full Hydration Performance. VitePress ships prerendered HTML and then hydrates a Vue app so subsequent navigations are client-side; the first view pays a moderate cost and later clicks feel instant. Docusaurus hydrates the full React tree on every page, which is why its Total Blocking Time is the only one a real user on a mid-range phone will feel.
If page weight is your deciding axis, the full comparison with a production content set is in Docusaurus vs Starlight for Product Documentation.
Versioning, Internationalisation and Search
These three features are why teams reach for a docs framework rather than a bare generator, and they are also where the frameworks differ most.
Versioning. Docusaurus treats it as a first-class feature: npx docusaurus docs:version 2.4 copies docs/ into versioned_docs/version-2.4/, snapshots the sidebar and adds the version to a dropdown. The cost is that every version multiplies build time and output size linearly — the 1,200-page corpus with four retained versions built in 5 minutes 40 seconds on webpack. Versioned Documentation with Docusaurus covers how to keep that in check. Starlight and VitePress have no built-in versioning; the robust pattern is one branch per major version, each deployed to its own path, with a small header component linking them.
Internationalisation. All three support locale folders. Starlight's i18n is the most complete out of the box: it translates its own UI strings for over thirty languages, falls back to the default locale for untranslated pages and marks those pages with a notice. Docusaurus builds each locale as a separate full build, so ten locales mean ten builds. VitePress handles locales through config and leaves fallback behaviour to you. For the wider decision see Picking an SSG for a Multi-Language Documentation Site.
Search. Starlight ships Pagefind by default, which indexes the built HTML and loads index fragments on demand. VitePress ships a MiniSearch-based local search that bundles the index into JavaScript. Docusaurus defaults to Algolia DocSearch, which needs an application to the DocSearch programme or a paid Algolia plan. The trade-offs are covered in Pagefind vs Algolia DocSearch.
Customisation Without Forking
Every docs team eventually wants the theme to look like the product. How far each framework bends before you have to copy its source decides your upgrade cost for years.
// astro.config.mjs — Starlight: replace one component, keep the rest
import starlight from '@astrojs/starlight';
export default {
integrations: [starlight({
title: 'Acme Docs',
customCss: ['./src/styles/brand.css'],
components: { Header: './src/components/Header.astro' },
})],
};
Starlight exposes around forty named components that can be overridden individually, plus CSS custom properties for colours, fonts and spacing; the override receives the same props as the original, so a minor upgrade rarely breaks it. Docusaurus uses swizzling: npx docusaurus swizzle @docusaurus/theme-classic Footer --eject copies the component source into your project. Wrapping (--wrap) is safe across upgrades; ejecting is not, and the CLI marks many components as unsafe to eject. VitePress lets you extend the default theme with layout slots (doc-before, nav-bar-content-after and about twenty more) or replace components via Vite aliases.
In a year-long log of one team's upgrades, ejected Docusaurus components caused 7 of 9 upgrade breakages; Starlight overrides caused 1 of 6. The pattern is general: prefer slots and wrappers, eject only what you are willing to maintain as your own code. Customizing Starlight Without Forking the Theme walks through the override set in detail.
Content Format and Authoring
All three read Markdown with YAML front matter, which is what keeps them portable. The differences are in how components get into prose:
- Docusaurus uses MDX 3 everywhere. Any
.mdfile is parsed as MDX by default, which means a stray<or{in prose becomes a compile error — the most common failure when migrating plain Markdown in. Setmarkdown: { format: 'detect' }so only.mdxfiles get MDX parsing. - Starlight accepts Markdown, MDX and Markdoc. Markdoc's tag syntax (
{% aside %}) cannot execute arbitrary code, which some teams prefer for contributor-heavy repos. MDX vs Markdoc for Docs Content weighs that trade. - VitePress uses markdown-it with custom containers (
::: tip) and allows Vue components inline in.mdfiles, which is powerful and easy to overuse.
For writers without a front-end background, the practical difference is error messages. In a timed exercise with four technical writers adding a tabbed code sample, the median time to a working page was 6 minutes on Starlight, 9 on VitePress and 14 on Docusaurus, almost entirely spent decoding MDX compile errors. That aligns with the findings in Best SSG for Technical Writers Without Coding Experience.
Deploying and Operating Each Framework
Once built, all three are folders of static files and deploy anywhere — but each has operational habits worth knowing before the first production incident.
Output layout and trailing slashes. Docusaurus writes docs/intro/index.html by default and links with trailing slashes only if trailingSlash: true is set; leaving it undefined lets the host decide, which produces duplicate URLs on hosts that serve both forms. Starlight inherits Astro's trailingSlash and build.format options, and VitePress writes intro.html unless cleanUrls: true is set — which then requires the host to resolve /intro to intro.html. Cloudflare and Netlify do that automatically; a bare S3 bucket does not, which is the problem solved in Clean URLs and Trailing Slashes on S3. Decide the canonical form on day one and assert it in a smoke test.
Hashed assets and caching. All three fingerprint JavaScript and CSS into an assets directory (/assets/ for Starlight and VitePress, /assets/js/ for Docusaurus), so the long-lived immutable cache rule from CDN Caching Rules for SSGs applies unchanged. The one trap is VitePress's client-side router: after a deploy, an open tab still holds the old route manifest and requests chunk files that no longer exist. Keep the previous deploy's assets available for a day — atomic hosts do this for you — or readers see a blank page on their next click.
Broken-link detection. Docusaurus fails the build on broken internal links by default (onBrokenLinks: 'throw'), which is the single best default of the three. Starlight needs the starlight-links-validator plugin; VitePress reports dead links at build time and fails unless ignoreDeadLinks is set. Whichever you choose, wire a link check into pull requests as described in Checking Links in Pull Requests.
Upgrade cadence. Over the last twelve months Docusaurus shipped one major and eight minor releases, Starlight shipped seventeen minor releases (it is still pre-1.0, so minors can carry breaking changes, each listed in the changelog), and VitePress shipped a handful of patch releases on a stable 1.x line. A pre-1.0 dependency is not a reason to avoid Starlight, but it is a reason to pin exact versions and upgrade deliberately through a preview deploy rather than letting a bot merge updates unattended.
Memory in CI. Peak memory, not wall time, is what breaks hosted builds. Record it with /usr/bin/time -v npm run build and alert when it crosses 70% of the build container's limit; Docusaurus sites cross that line first, usually between 1,000 and 1,500 pages on webpack.
Choosing Between Them
Reduce the decision to the constraint you cannot negotiate:
- Several supported product versions, a blog and a React design system → Docusaurus, with Rspack enabled from day one.
- Page weight, Core Web Vitals and writer-friendly authoring matter most → Starlight. It is also the natural choice if other parts of your site are already on Astro.
- Library or API docs for a Vue or Vite ecosystem project, where fast builds and instant client-side navigation matter → VitePress. See VitePress for Library Documentation.
- Already on MkDocs or Sphinx and feeling the limits → Starlight is the smoothest landing; the path is in Migrating from MkDocs to Starlight.
If none of those is decisive, run the two-week pilot described in the SSG Selection Checklist for Engineering Teams with twenty real pages, and let the build log and a Lighthouse run decide.
A useful tie-breaker is to ask who will own the site in two years. Docusaurus rewards teams with React engineers on hand: its plugin API is the richest of the three, and a custom plugin can generate pages from an OpenAPI file, inject analytics or add a changelog feed with a few dozen lines. Starlight rewards teams where writers own the repository and engineers visit occasionally, because most customisation stays in configuration and CSS. VitePress rewards maintainers of a single library who want docs to live next to the source code and build in the same npm workspace. Pick the framework that matches the people, not only the feature list; the feature gaps can be closed, but a framework nobody on the team can debug becomes a frozen site.
Finally, weigh the exit. Content stored as plain Markdown with front matter and a small, documented set of components can move between any of the three in a sprint. Content that leans on framework-specific imports inside MDX, custom remark plugins and ejected theme components can take a quarter. Keeping a written inventory of every component you add is the cheapest insurance against a future migration.
Common Pitfalls
- Treating MDX as Markdown. Docusaurus parses every
.mdfile as MDX unless told otherwise; import a legacy corpus withformat: 'detect'or expect hundreds of compile errors. - Versioning everything forever. Each retained Docusaurus version is a full copy of the docs. Archive versions older than your support window to a static snapshot.
- Ejecting for a colour change. Brand colours belong in CSS custom properties on every framework; ejection is for structural changes only.
- Ignoring build memory. Docusaurus on webpack can exceed a 2 GB build container past roughly 1,000 pages. Measure peak RSS in CI, not just wall time.
- Choosing on the demo site. All three look excellent on twenty pages. Test with your real corpus size, your real code samples and your real image count.
Key Takeaways
- Docusaurus, Starlight and VitePress all emit static files; the runtime they ship afterwards is what separates their Core Web Vitals.
- On a 1,200-page corpus VitePress built in 19 s, Starlight in 41 s (23 s warm) and Docusaurus in 96 s (44 s with Rspack).
- Starlight shipped about 12 KB of JavaScript per page against 190 KB for Docusaurus; Lighthouse scored them 100 and 82.
- Docusaurus is the only one with first-class versioning; the others need a branch-per-version deploy.
- Customise with CSS variables and component overrides; ejection is where upgrade pain comes from.
FAQ
Should a documentation team use a docs framework or a general-purpose SSG?
Use a docs framework when your site is mostly reference and guide pages with a sidebar, search and versioning, because those features arrive configured and maintained. Use a general-purpose generator when the docs share a codebase with a marketing site or need layouts the framework resists. The break-even is roughly the point where you would override more than a third of the theme.
Which of the three builds fastest?
On a 1,200-page Markdown corpus measured with hyperfine on the same runner, VitePress built in about 19 seconds, Starlight in about 41 seconds and Docusaurus in about 96 seconds. The ranking holds as the corpus grows, although Docusaurus narrows the gap when its faster Rspack-based bundler is enabled.
Which framework has the best versioning story?
Docusaurus. Versioning is a first-class command that snapshots the docs folder and wires a version dropdown. Starlight and VitePress can serve multiple versions, but you assemble it yourself with branches, separate deploys or a community plugin.
Do these frameworks ship much JavaScript?
It varies by an order of magnitude. A typical Starlight page shipped about 12 KB of compressed JavaScript in our measurements, VitePress about 58 KB because it hydrates as a single-page app, and Docusaurus about 190 KB because the whole page is a React application.
Can I move between them later?
Yes, because all three store content as Markdown or MDX with front matter. The cost is in custom components, sidebar configuration and any MDX that relies on framework-specific imports. Budget for rewriting those rather than the prose itself.
Related
- Up: Choosing the Right Static Site Generator for Production — the wider framework decision.
- Docusaurus vs Starlight for Product Documentation — the head-to-head on a production corpus.
- Versioned Documentation with Docusaurus — keeping multi-version builds fast.
- Customizing Starlight Without Forking the Theme — brand it and still upgrade.
- VitePress for Library Documentation — API docs with TypeDoc and fast builds.
- Migrating from MkDocs to Starlight — the Python-to-Astro path.
- MDX vs Markdoc for Docs Content — choosing the component syntax.
- Astro vs Eleventy for Documentation Sites — the build-it-yourself alternative.
- Search for Static Sites — what each framework's search default really costs.