Astro vs Eleventy for Documentation Sites

Astro and Eleventy are both excellent for documentation, but they make different bets. Astro gives you a component model and islands for the occasional interactive widget; Eleventy stays closer to plain templates-and-Markdown and ships no JavaScript at all unless you add it. This comparison covers project setup, Markdown handling, CI, asset optimization, and the bytes that actually reach the reader — each with the config you'd ship and a before/after number where it matters. For the broader framework decision this sits inside, start from Choosing the Right Static Site Generator for Production.

Choosing Astro or Eleventy for a documentation site A decision flow starting from whether the docs need interactive components, branching to Eleventy for pure Markdown sites and Astro for sites with islands or type-safe content collections, with both paths converging on fast static output. Which fits your docs? Interactive islands or type-safe content needed? Eleventy Pure Markdown + Nunjucks Zero default JavaScript leanest pipeline to maintain Astro Components + islands Content collections (Zod) hydrate only what's interactive Fast static HTML either way No Yes
Start from whether the docs genuinely need interactivity or schema-validated content: if not, Eleventy's leaner pipeline wins; if so, Astro's islands and collections earn their weight. Both end at fast static HTML.

Project Setup & Routing

Astro has an interactive scaffold; Eleventy is added to an existing project as a dependency (there is no create command):

# Astro: interactive project scaffold
npm create astro@latest docs-site

# Eleventy: add to a project you've already initialized
npm install @11ty/eleventy --save-dev

Astro maps routes from src/pages/, and file-based routing means a src/pages/guide/intro.md becomes /guide/intro/ with no extra config. Eleventy treats your input directory (default: the project root, commonly set to src) as the route source, and derives the output path from each file's location plus any permalink you set. Set your base path early so asset links don't break on a staging subpath. For docs specifically, the practical difference is small: both give you clean nested URLs from a folder tree, and both let you override a slug per page.

Where the setups diverge is the surrounding scaffold. Astro's create flow drops a working project — layouts, a Markdown page, a config — so you're editing content within minutes. Eleventy starts from nothing, which is more assembly up front but means there's no generated code you didn't write and no convention you have to unlearn. For a small team that values understanding every line of its build, Eleventy's blank slate is a feature; for a team that wants a sensible default layout and a typed config out of the box, Astro's scaffold saves a day. Neither choice is reversible-cheap once the site is large, which is why it belongs at the start of the project, not the middle.

Markdown Processing & Collections

Astro processes Markdown/MDX through the remark/rehype ecosystem. Add plugins in config (import each plugin you reference):

// astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import remarkGfm from 'remark-gfm';
import rehypeSlug from 'rehype-slug';

export default defineConfig({
  integrations: [mdx()],
  markdown: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [rehypeSlug], // adds id="" anchors to headings
  },
});

Astro's real docs advantage is content collections: a Zod schema validates every page's frontmatter at build time, so a missing title or a malformed version fails the build with a precise error instead of rendering a blank page. Eleventy builds the same kind of grouping with collections, though without the schema layer — here, versioned docs sorted by a frontmatter version field:

// .eleventy.js
module.exports = function (eleventyConfig) {
  eleventyConfig.addCollection("docs", (collectionApi) =>
    collectionApi
      .getFilteredByGlob("src/docs/**/*.md")
      .sort((a, b) => a.data.version - b.data.version)
  );
};

If you want Eleventy to fail on bad frontmatter the way Astro does, add a small Zod or JSON-Schema check in the build script — it's a few lines, but it's a step Astro gives you for free. On a large docs set this distinction compounds: with hundreds of contributors and pages, an unvalidated version field or a typo'd category silently produces a wrong route or an empty listing, and you find out from a reader, not the build. Astro's collections turn that class of error into a precise, file-and-line build failure, which on a team of mixed experience is worth more than any raw-speed advantage.

Both frameworks support the standard docs features through the same Markdown ecosystem: GitHub-flavored tables and task lists via remark-gfm, heading anchors via rehype-slug, and syntax highlighting (Astro ships Shiki in core; Eleventy uses a plugin such as @11ty/eleventy-plugin-syntaxhighlight). The authoring experience for a writer is nearly identical — they write Markdown — so the differences that matter are the ones developers feel: schema validation, component reuse, and how the build behaves when a contributor gets something wrong.

CI/CD Integration

Both build fine on hosted CI. Cache the npm download directory so installs don't re-fetch every run:

name: Deploy Docs
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci && npm run build

setup-node's cache: npm restores ~/.npm keyed on your lockfile, which is the simplest reliable speedup for both frameworks. On a representative docs repo, that cache cut a cold install from roughly 38s to 9s:

StepNo cachecache: npm
npm ci38s9s
Eleventy build (1,200 md pages)14s14s
Astro build (1,200 md pages)31s31s

The build itself doesn't get faster from dependency caching — for that you cache the framework's own asset cache. To track build-speed regressions over time, see How to Benchmark Hugo vs Astro Build Speeds.

Asset Optimization

Docs are screenshot-heavy, so optimize images at build time. Astro's built-in <Image /> from astro:assets handles this with no extra package (the old @astrojs/image integration was removed when image support moved into core in v3). Eleventy uses eleventy-img, which wraps Sharp to generate resized, modern-format outputs. Both emit WebP/AVIF and let the browser pick the smallest variant:

ApproachHero (1200px) deliveredLCP, throttled mobile
Raw PNG screenshot1.1 MB2.9s
Astro <Picture> (AVIF+WebP)145 KB1.7s
Eleventy eleventy-img (WebP)190 KB1.8s

The optimization story is essentially a tie; the deeper treatment of the Astro pipeline lives in Image Optimization Pipelines in Astro.

Bytes Shipped & Hydration

This is the axis where the two genuinely differ. A purely static docs page built with Eleventy ships 0 KB of JavaScript — there is no client runtime. Astro also ships 0 KB on a page with no islands, but the moment you hydrate a component you opt into its cost. Keep JavaScript honest: in Astro, hydrate only genuinely interactive components (a search box, a version switcher) with client:visible or client:idle, never client:load on static content. In Eleventy, reach for Alpine.js or a tiny vanilla script for the same widgets.

JavaScript bytes shipped: Eleventy versus Astro Two page panels. The Eleventy panel ships static HTML and zero client JavaScript by construction, with any script hand-written. The Astro panel also starts at a zero-kilobyte baseline, then adds cost per hydrated island: about fourteen kilobytes for a search island with client:visible, and thirty-eight wasted kilobytes if client:load is left on static content. What reaches the browser: JavaScript bytes shipped Eleventy templates + Markdown → HTML Static HTML always shipped Client JS: 0 KB only what you write & paste in 0 KB JS by construction Astro components + islands Static HTML + 0 KB JS baseline + search island · ~14 KB hydrated with client:visible + stray client:load · +38 KB shipped on static content for nothing payload grows per hydrated island bytes accrue Both start at 0 KB — the divergence is per-island cost, not a baseline runtime.
Neither framework ships a client runtime on a static page. Eleventy stays at zero because every byte of JavaScript is hand-written; Astro stays at zero until you hydrate, then each island adds only its own cost — unless a stray client:load ships a framework onto static content for nothing.

The measured cost is concrete. A docs page with one interactive search box and a theme toggle lands very differently depending on how you wire it:

Page compositionAstroEleventy
All-static prose page0 KB JS0 KB JS
+ search island (client:visible)~14 KB gzip~12 KB (Alpine + index script)
+ accidental client:load on a static sidebar+38 KB for nothingnot possible without hand-writing it

The third row is the whole point: Eleventy has no mechanism to accidentally ship a framework, because every byte of client code is something you wrote and pasted in. This contrasts sharply with a framework like Next.js: even a fully static export hydrates the React runtime on every page unless you actively strip it, so its floor is higher than either of these — see Next.js Static Export for Content Sites for where that trade-off lands on a docs build.

The difference is one of guardrails, not ceilings. Eleventy makes the lean path the default because there is no easy way to accidentally ship a framework — any client code is something you wrote and pasted in deliberately. Astro makes the lean path the default too, but it also makes shipping a heavy island a one-word change, so the discipline has to be cultural: a code-review rule that flags client:load, or a Lighthouse budget in CI that fails when a docs page's JavaScript crosses a threshold. With that guardrail in place, an Astro docs site stays as lean as an Eleventy one while keeping the option to drop in a rich interactive component when a page genuinely needs it.

The practical guidance: a static docs site can be byte-for-byte identical on either framework, so let component ergonomics and content validation — not hydration — decide. If you're weighing plugin and runtime overhead during a migration, the Jekyll Plugin Ecosystem review is a useful contrast for what an aging ecosystem costs, and the SSG Framework Selection Matrix is where you weigh these factors against each other instead of arguing them one at a time.

How each generator turns Markdown into a page Two pipelines. Eleventy reads Markdown through the data cascade into a template engine and writes HTML. Astro reads Markdown into a typed content collection, renders components at build time and optionally hydrates islands, then writes HTML plus any island bundles. Same input, two rendering models Eleventy Markdown data cascade template → HTML Astro Markdown typed collection schema-validated components → HTML + islands if asked The validation step is Astro's structural advantage; the shorter pipeline is Eleventy's
Eleventy renders through a template engine; Astro renders through components with an optional hydration step. Everything else in this comparison follows from that difference.

The pipelines also fail differently, which matters more day to day than either benchmark. An Eleventy page missing a front-matter field renders with a blank where the value should be; an Astro page missing a schema-required field stops the build and names the file. On a site with a handful of maintainers the second behaviour is worth real build time, because the alternative is discovering an empty description in a search result three weeks later. On a site where the corpus is stable and the contributors are few, the first is perfectly adequate and considerably simpler to reason about.

Search, Navigation & Versioning

The features that make documentation usable — search, a navigation tree, and version switching — are where the two frameworks ask for different amounts of work, and where the comparison stops being abstract.

For search, both lean on a static index rather than a server. The common pattern is a build step that walks the rendered pages, extracts headings and body text, and writes a JSON index that a tiny client-side library (Pagefind, Lunr, or a hosted index) queries in the browser. Pagefind is framework-agnostic and runs as a post-build step against the dist/_site output, so it works identically on Astro and Eleventy — which means search shouldn't decide the framework. What differs is the navigation UI: in Astro you'd typically build the search box as an island hydrated with client:visible, while in Eleventy you'd wire the same input with a small Alpine or vanilla script. Both ship comparable bytes; the Astro version is a component you can reuse, the Eleventy version is a script you maintain by hand.

Navigation trees come from the same data on both. Astro reads its content collection and renders a sidebar from the entries' slugs and frontmatter order; Eleventy reads a collection and does the same in a Nunjucks partial. The ergonomic edge is Astro's when the tree is complex — a typed collection means the sidebar can't reference a page that doesn't exist, because the collection is the source of both the route and the nav. In Eleventy that invariant is something you enforce yourself.

Versioned docs is the case that most clearly separates them. Astro generates a route per version with getStaticPaths over a version-keyed collection, and the schema validates each version's frontmatter as it builds. Eleventy produces the same routes from a collection sorted on a version field, but without the schema guard a mislabeled version silently lands in the wrong place. For a single current version the two are equivalent; for a docs set that maintains three or four live versions, Astro's validation is the difference between a build error and a support ticket.

Common Pitfalls

  • Over-hydrating in Astro: adding client:load to non-interactive content ships JavaScript for nothing. Default to no directive; reach for client:visible/client:idle only when needed.
  • Eleventy permalink conflicts: custom doc slugs break default pagination. Set an explicit permalink in frontmatter to keep nested URLs stable.
  • Skipping frontmatter validation in Eleventy: without a schema check, a malformed page renders blank instead of failing the build. Add a Zod/JSON-Schema guard to match Astro's collections.
  • Cold CI builds: without dependency caching, every run re-installs from scratch. Cache ~/.npm (and Eleventy's .cache/ if you use eleventy-img).

Conclusion

Choose Astro if you want a component model, type-safe content collections, and the freedom to drop in an interactive island where a docs page genuinely needs one — accepting a heavier build and the discipline of keeping hydration in check. Choose Eleventy if you want the leanest possible Markdown-to-HTML pipeline, a build with nothing in it you didn't write, and zero default JavaScript by construction. For a docs set that is mostly static prose, the reader-facing output is effectively identical, so decide on the developer experience your team will live with: schema validation and component reuse on one side, minimalism and full transparency on the other. Whichever you pick, gate the build on a JavaScript budget and validate frontmatter, and the framework choice stops being something you can get badly wrong.

One practical note on mixing the two. Nothing stops a documentation site from generating its reference section with one tool and its guides with another, joined by a path prefix at the edge. It costs a routing rule and it removes the compromise where a component-heavy guide section drags a thousand-page reference into a slower build. Treat that as a legitimate option rather than an admission that the choice was wrong.

Key Takeaways

  • Eleventy builds raw Markdown faster and ships the leanest possible pipeline; Astro adds component compilation but gives you islands and type-safe collections.
  • On a docs site with no interactive components, both ship zero JavaScript — the difference appears only when you hydrate.
  • Astro's content collections validate frontmatter at build time for free; replicate it in Eleventy with a small schema check.
  • Image optimization is effectively a tie: astro:assets versus eleventy-img, both emitting modern formats with measurable LCP wins.
  • Let component ergonomics and content validation decide, not benchmark seconds — both produce fast static docs.

FAQ

Which SSG builds large documentation repositories faster?

Eleventy is typically faster on raw Markdown-heavy builds because it does less per page — no component compilation step. Astro's build does more work compiling components but gives you the islands model in return. On a few thousand Markdown pages Eleventy often finishes in roughly half Astro's time, though both stay well inside a normal CI window.

How do I implement versioned documentation in Astro?

Use getStaticPaths to generate one route per version, backed by content collections keyed on the version directory. The collection schema validates each version's frontmatter at build time, so a malformed version page fails the build instead of shipping a broken route.

Can Eleventy support interactive documentation components?

Yes. Add Alpine.js or a small vanilla script through a <script> tag or an Eleventy passthrough copy. You get interactivity such as a search box or a theme toggle without shipping a full framework runtime, which keeps the default zero-JavaScript baseline intact.

What CI caching minimizes documentation build times?

Cache the npm download directory keyed on the lockfile for installs, plus any framework cache directory such as Eleventy's image cache or Astro's processed-asset cache. Dependency caching alone often removes 20 to 40 seconds from a cold run on both frameworks.

Does Astro ship JavaScript on a docs site that has no interactive components?

No. Astro ships zero JavaScript until you add a client directive to a component. A purely static documentation page built with Astro delivers the same empty JavaScript payload as the equivalent Eleventy page, so the bytes-shipped difference only appears once you start hydrating islands.