Choosing the Right Static Site Generator for Production

Choosing a static site generator (SSG) for production is mostly an exercise in matching three things: how fast your content changes, how long your CI/CD pipeline can run, and what your team already knows. Raw benchmarks matter far less than whether the framework fits those constraints a year from now, when the site is ten times larger and the original author has moved on. This guide is for engineers, documentation teams, and indie hackers who are about to commit a project to one generator for years and want to decide deliberately rather than by reputation.

The five generators worth comparing for production content sites are Astro, Eleventy (11ty), Hugo, Jekyll, and a Next.js static export. They differ along four axes that actually predict how the project ages: how much JavaScript reaches the browser, how fast a cold build runs, what the authoring experience feels like, and how healthy the plugin ecosystem is. We walk through the decisions in the order they bite — requirements, rendering model, ecosystem, build performance, content modeling, and delivery — and tie each claim to something you can measure.

Five static site generators across the axes that matter A comparison grid scoring Astro, Eleventy, Hugo, Jekyll, and a Next.js static export on JavaScript shipped to the browser, cold build speed, authoring experience, and ecosystem health, with higher fill meaning stronger on that axis. Five generators, four production axes Taller bar = stronger on that axis (less JS shipped is better) Astro islands Eleventy templates Hugo Go Jekyll Ruby Next export React Low JS Build Author Ecosys
No generator wins every axis: Hugo dominates build speed, Eleventy and Hugo ship the least JavaScript, a Next.js export trades runtime weight for React's ecosystem, and the right pick is the one whose strong axes match your constraints.

What You Will Learn

This guide is organized around the production decisions that recur on every project, each with its own deep-dive:

The two sibling guides — Performance Optimization & Core Web Vitals for SSGs and Production-Ready Deployment & CI/CD Workflows — pick up where framework choice ends: making the site fast and shipping it safely.

If you are already on the wrong one

Choosing well matters most before a site exists; most teams arrive at this question with a corpus already in place. Moving between generators is a well-understood, staged piece of work rather than a rewrite — inventory, mechanical conversion, template porting, URL verification, reversible cutover — and it is covered end to end in Migrating Between Static Site Generators. Read that before concluding that a bad fit is permanent, because the cost is usually weeks rather than quarters and the URL contract is the only part that is genuinely unforgiving.

Defining Production Requirements & Constraints

Write down your constraints before you compare frameworks, because they eliminate most options for you.

Start with your pipeline. Hosted CI runners impose wall-clock limits — GitHub Actions allows up to 6 hours per job, but most teams set a far shorter timeout, and a cold full build of a large site can blow past a 10-minute budget. If your build can't finish reliably inside that window, you need a framework with incremental builds, not a faster laptop. Measure it once: run a full production build on a representative content set and time it with hyperfine 'npm run build'. A 2,000-page Markdown site that builds in 18s on Hugo might take 70-110s on Astro and several minutes on Jekyll — the absolute numbers vary, but the ranking is consistent enough to plan around.

Match the framework's core language to your team. Hugo is Go, Jekyll is Ruby, and Astro and Eleventy are JavaScript. The maintenance cost of a framework is the cost of the language nobody on your team wants to debug at 2am. A team that lives in Node will be far more productive extending an Eleventy config than fighting a Ruby gem dependency, even if Jekyll is technically capable.

Finally, document how often content changes and who changes it. A docs site that publishes hourly from a CMS has different needs than a marketing site rebuilt on every commit — the former benefits from incremental builds and preview deploys; the latter barely notices build time at all. Content velocity also decides how much the authoring layer matters: if non-developers publish daily, a Git-backed visual editor and schema-validated frontmatter will save more engineering time than any build optimization. Capture all of this in three lines before you look at a single benchmark — the framework you reach for after writing those constraints is usually right, and the one you reach for from reputation usually isn't.

When you map technical constraints to a defensible choice, the SSG Framework Selection Matrix gives you a scoring frame so the decision survives review. The matrix is also where you encode the soft constraints — hiring, existing internal tooling, the language your platform team already supports — that a raw benchmark can't see but that dominate the total cost of ownership over the life of the site.

Architecture & Rendering Paradigms

The rendering model determines your performance ceiling, your hosting bill, and how much JavaScript reaches the browser — the first axis in the diagram above.

Full static generation renders every page at build time. It has zero server cost and the best possible time-to-first-byte, but personalization and frequently changing data force full rebuilds. Template-only generators — Eleventy and Hugo — emit plain HTML and CSS with no client runtime at all, so they ship zero JavaScript unless you add a script yourself. Islands architecture (Astro, and increasingly others) ships static HTML and hydrates only the interactive components, keeping JavaScript off pages that don't need it. A Next.js static export gives you React's component model with pre-rendered output, but you pay for the framework runtime on every interactive page — that is the heaviest JavaScript baseline of the five, and it is exactly the trade-off weighed in Next.js Static Export for Content Sites.

A useful default: generate everything you can at build time, hydrate islands only where there's genuine interactivity, and reserve per-request logic for the handful of routes that truly need it. Because JavaScript on the main thread is the main driver of Interaction to Next Paint (INP), this single decision sets your Core Web Vitals ceiling — see Performance Optimization & Core Web Vitals for SSGs for how the metrics break down per framework.

The rendering model also constrains your hosting story. A fully static build is just files: it can sit on any CDN, in object storage, or behind a free static host, and it rolls back instantly because the previous build is still a set of files. The moment you depend on per-request rendering you've added a runtime — a function that cold-starts, costs money per invocation, and has to be monitored and rolled back like any server. For a content site that's a real liability you take on deliberately, not by default. The discipline that keeps a project on the cheap, fast, static path is resisting the temptation to reach for a server function the first time a feature is slightly easier with one; most of those features (search, comments, forms) have a static or third-party-API answer that keeps the output pre-rendered.

CDN behaviour is part of this decision, not an afterthought. The example below configures Astro to inline small stylesheets and serve hashed assets from a CDN prefix, plus a redirect that preserves a legacy URL after migration. Note that redirects is a top-level Astro config key — it does not live under build:

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  build: {
    // Inline small CSS into the page; link larger stylesheets.
    inlineStylesheets: 'auto',
    // Prefix hashed build assets so they're served from your CDN.
    assetsPrefix: 'https://cdn.example.com',
  },
  // Top-level: preserve old URLs during a migration.
  redirects: {
    '/legacy-docs': '/docs',
  },
});

Inlining small stylesheets removes a render-blocking request, while assetsPrefix points hashed assets at a CDN origin so they cache aggressively and independently of your HTML. Caching headers themselves are configured at your host or CDN, not here.

Build Performance & Incremental Builds at Scale

Build performance only matters at scale, but at scale it dominates everything — and it is the axis where the five generators diverge most.

Hugo is the outlier. Its Go engine renders thousands of pages per second and routinely builds 10,000-page sites in seconds where Node-based generators take minutes. That gap is the whole reason Hugo keeps winning large-repository projects, and Hugo Build Times for Large Repositories walks through the benchmarks and the tuning that closes or widens it. For most other generators the single biggest lever is incremental builds and shared build caching: rebuilding only the pages affected by a change instead of the whole site, and persisting that work between CI runs. Beyond that, parallel asset processing (image transforms, font subsetting) keeps CPU-bound steps from serializing, and caching the framework's build cache between commits avoids re-doing work you already did.

Before optimizing, measure. Hugo, for example, can report per-template timing so you can see which layouts actually cost you:

hugo --gc --minify --templateMetrics --templateMetricsHints

--gc clears unused cache entries, --minify compresses output, and the template-metrics flags print an execution-time table per template — the fastest way to find the one partial that's being rendered ten thousand times. The same instinct applies to every generator: profile before you optimize, because the bottleneck is almost never where you'd guess. On Node-based generators it's usually image processing or an expensive Markdown plugin running on every page; on Jekyll it's frequently a slow Liquid loop or an unbounded where/sort filter over a large collection.

There's a second, quieter cost that benchmarks miss: the relationship between build time and developer feedback. A 90-second build is fine in CI but miserable in local development, where you rebuild dozens of times an hour. Generators with fast incremental dev servers — Astro's and Eleventy's hot reload, Hugo's sub-second rebuilds — keep authors in flow, while a slow cold build taxes every edit. Weigh the dev-loop experience alongside the CI number; they're different metrics that the same framework can score very differently on. The CI side of this — caching dependencies, sharing build caches across runners, and gating on preview deploys — is the subject of Production-Ready Deployment & CI/CD Workflows.

Ecosystem Maturity & Plugin Stability

A framework's plugin ecosystem is where projects quietly rot, so evaluate it like a dependency you'll own for years — the fourth axis in the overview.

Prefer official or first-party plugins for anything in the critical build path; they tend to track the framework's release cadence, while community plugins lag or get abandoned. Before adopting a third-party plugin, check its last release date, open-issue trend, and whether it pins to a framework version that's about to change. The ecosystems differ sharply: Jekyll's is mature but aging, with several once-essential plugins now unmaintained; Astro's is younger but moves with the core team; Hugo deliberately keeps most capability in core rather than plugins. The Jekyll Plugin Ecosystem review maps which Jekyll integrations are still safe to depend on and which signal it is time to migrate.

Security lives here too. Build-time plugins run with access to your repository and environment, so an unmaintained dependency is a real exposure, not a hypothetical one. A compromised or abandoned build plugin can read your environment secrets, write arbitrary files into the output, or pull in transitive dependencies you never audited. Confirm the framework and its core plugins follow semantic versioning so a minor upgrade doesn't break your build without warning, pin versions in a lockfile, and run a dependency audit in CI so a newly disclosed vulnerability fails the build instead of shipping silently.

A practical way to score ecosystem health is to count how many of your required capabilities are met by the framework's core versus by third-party code. A generator that handles images, syntax highlighting, sitemaps, and feeds in core leaves you with a small, auditable dependency surface. One that needs a community plugin for each of those is four maintenance relationships you didn't choose, any of which can go stale between framework releases. Fewer dependencies isn't a vanity metric here — it's directly proportional to how much unplanned work the project generates over its life.

Content Modeling & Developer Experience

Good content modeling is what keeps a site editable after it grows past a few hundred pages — and the authoring axis is where Astro and Jekyll tend to score above the others.

Validate your content schema at build time rather than discovering a malformed page in production. Type-safe content — Astro's content collections, or a Zod/JSON-Schema check in any framework — turns a broken frontmatter field into a build error instead of a blank page. Reusable layouts and components keep templates from drifting as the site expands.

The Eleventy config below restricts which file types are processed and sets a default layout for every page, which is the kind of small, explicit convention that prevents surprises later:

// .eleventy.js
module.exports = function (eleventyConfig) {
  eleventyConfig.addGlobalData("layout", "docs.njk");
  eleventyConfig.setTemplateFormats(["md", "njk"]);
  return { dir: { input: "src", output: "dist" } };
};

This sets docs.njk as the global default layout, processes only Markdown and Nunjucks files, and fixes the input/output directories so builds are predictable across machines. When you're architecting a content-heavy docs platform, Astro vs Eleventy for Documentation Sites compares the routing and schema-validation trade-offs in detail, and if your authors aren't developers, Best SSG for Technical Writers Without Coding Experience reframes the whole decision around the authoring layer.

Core Web Vitals: LCP, CLS & INP for Static Sites

Because the rendering model sets the JavaScript baseline, the five candidates line up predictably on field performance — but each of the three Core Web Vitals has a different dominant cause, and only one of them is really about framework choice.

Interaction to Next Paint (INP) is the metric framework choice moves most, because it is gated by main-thread JavaScript. Eleventy and Hugo ship no client runtime, so a content page's Total Blocking Time is whatever third-party scripts you add — zero if you add none. Astro matches that baseline on static pages and only pays for the islands you hydrate, which keeps INP low as long as you avoid client:load on presentational markup. A Next.js static export is the outlier: even an export ships React's runtime and the per-page hydration code, so an interactive route carries tens of kilobytes of JavaScript that must parse and execute before the page responds. None of this means the export is slow — it can score well — but it starts from a heavier position and demands more discipline. Which components hydrate, and when, is the whole game here; the mechanics of JavaScript hydration and partial rendering show how to keep interactive routes under budget without giving up the component model.

Largest Contentful Paint (LCP) is mostly about your assets and their delivery, not the generator. Every one of the five can hit a fast LCP because they all emit pre-rendered HTML, so the byte that decides the score is usually the hero image or web font, plus how quickly the CDN serves the document. The framework-agnostic fixes — preloading the LCP image, right-sizing it, and avoiding a font swap that repaints the headline — are covered in Largest Contentful Paint optimization for static sites.

Cumulative Layout Shift (CLS) is a discipline problem that no framework solves for you: reserve dimensions for images and embeds, and load fonts so a fallback swap doesn't reflow the page. A template-only generator won't shift less than a hydrated one if both omit width/height on images.

The practical order of operations is: pick the rendering model to set your INP ceiling, then treat LCP and CLS as asset and layout work that applies regardless of the generator. The full per-metric breakdown lives in the sibling guide, Performance Optimization & Core Web Vitals for SSGs.

Image, Font & Asset Optimization Pipelines

Framework choice decides how much of the asset pipeline you get for free. The heaviest bytes on a content page are almost always images and fonts, so a generator that transforms them at build time — instead of shipping the originals — saves you from bolting on the same machinery by hand.

Build-time asset pipeline from source files to the browser A left-to-right flow: raw source assets (an oversized image and a full font) pass through a build-time transform stage that generates a responsive srcset, encodes AVIF and WebP, subsets the font to WOFF2, and content-hashes filenames; the hashed output is served from a CDN with a one-year immutable cache and reaches the browser as the smallest possible bytes with no font swap. Where the heavy bytes get cut Transform once at build time, then cache forever — nothing runs per request Source assets hero.png 3000 px · unstripped brand.woff full charset oversized pixels · unused glyphs Build-time transform Responsive srcset 320–1600 px AVIF / WebP encode + fallback WOFF2 subset to used glyphs Content-hash in filename strips metadata · runs once, not per request CDN immutable max-age 1 year no repeat cache misses Browser smallest bytes no font swap
The whole pipeline runs at build time: the transform stage strips oversized pixels and unused glyphs, content-hashing lets the CDN cache the result for a year, and the browser receives the smallest possible bytes with no layout-shifting font swap.

For images, the pipeline you want generates multiple responsive sizes, encodes modern formats (AVIF/WebP with a fallback), strips metadata, and emits a content-hashed filename so the file can be cached forever. Astro ships this in core through its image component and integrations; Eleventy has a first-party image plugin that does the same; Hugo has image processing built into its template functions; Jekyll and a Next export lean on plugins or the framework's own loader. Doing it at build time — rather than at request time behind a function — keeps the output fully static and cacheable. The concrete recipe, including how to wire responsive srcset output and where the transforms actually run, is in image optimization pipelines in Astro.

For fonts, the two levers that move LCP and CLS are subsetting (ship only the glyphs the site uses, in WOFF2) and a load strategy that avoids a visible swap — font-display: optional or a preloaded, size-matched fallback. Self-host the files so they inherit your CDN's immutable caching instead of making a cross-origin request on every first visit. The trade-offs between swap, optional, and a preloaded fallback are worked through in font loading strategies for static sites.

The rule that ties images, fonts, CSS, and JS together is fingerprinting: every asset gets a content hash in its filename, which is what makes the aggressive, year-long immutable caching in the next section safe. Change the file, change the hash, change the URL — no cache invalidation required.

Three questions that eliminate most candidates Three panels. Scale asks how many pages and how often they change. Team asks which languages and workflows people already know. Surface asks whether pages need interactive components or are pure documents. Each answer eliminates candidates before any benchmark is run. Three questions that eliminate most candidates Scale how many pages, growing how fast? how often does the whole site rebuild? is the build in the deploy path? who waits for it — CI or a writer? Team which languages are already known? who edits content day to day? is there an existing design system? who will own this in two years? Surface pure documents, or components? any interactivity per page? shared UI with a product app? what must the build validate? Benchmarks come last — they compare the finalists rather than choosing them.
Answering these three honestly usually leaves two candidates, which is a short enough list to benchmark properly.

Edge & CDN Delivery: Caching and Headers

The framework produces static files; everything after that is delivery, and this is where a content site earns most of its speed. Distribute through a CDN to cut Time to First Byte, and apply the two-tier cache policy that a fingerprinted build makes safe: hashed assets get Cache-Control: public, max-age=31536000, immutable because their URL changes whenever their content does, while HTML documents get a short max-age (or no-cache with revalidation) so a new deploy is visible immediately. Getting those two headers right is most of the performance difference between a fast static site and a slow one — the exact header values, and the CDN behaviours that override them, are in CDN caching rules for SSGs, with a platform-specific walkthrough in Cloudflare Pages edge caching setup.

The framework choice rarely eliminates a host — every major platform builds every major generator — but a host's incremental-build support, edge network, and header configuration can break a tie between two otherwise-equal candidates. When the delivery layer, not the framework, is the deciding factor, Netlify vs Vercel deployment strategies compares what each platform gives you for free.

CI/CD, Preview Deploys & Rollbacks

The last constraint is the one from the very first section, closed: your build has to finish inside the CI window on every commit, and a bad deploy has to be reversible in seconds. Automate the build so it runs on every push, cache dependencies and the framework's build cache across runs so cold builds stay rare, and gate merges on a performance budget so a regression fails the check instead of shipping. Setting that up on the most common runner is covered in GitHub Actions for automated SSG builds.

Two workflow features turn a static build from safe-in-theory into safe-in-practice. Preview deploys build every pull request to its own URL, so reviewers see the rendered change and the performance check runs before merge, not after — the setup is in preview environments for pull requests. Rollback is nearly free because the output is just files: the previous build is still a complete, immutable set of assets, so redeploying it is instant and total, with no database migration or server state to reconcile. That property — cheap, atomic, instant rollback — is one of the strongest reasons to keep a content site on the static path in the first place. The full treatment of preview environments, build hooks, and atomic deploys lives in the sibling guide, Production-Ready Deployment & CI/CD Workflows.

Common Pitfalls

  • Choosing on benchmarks alone: raw build speed only dominates in the thousands of pages. Below that, authoring experience and ecosystem health matter more than a few seconds of build time.
  • Hydrating SEO-critical pages on the client: it raises TTFB, hurts Core Web Vitals, and throws away the benefit of static generation. Use static HTML with progressive enhancement, or hydrate only true islands.
  • Ignoring CI/CD timeouts when picking a framework: a large repo that only does full rebuilds will eventually fail its pipeline. Require incremental builds or split the build across nodes.
  • Adopting unmaintained plugins: abandoned build-time dependencies become security exposures and break on the next framework upgrade. Check maintenance status before you commit.
  • Hardcoding secrets in build scripts: this leaks them into git history and makes environment switching painful. Use your platform's secret injection instead.

Key Takeaways

  • There is no single "best" SSG — there's the one whose strong axes (JS shipped, build speed, authoring, ecosystem) match your constraints.
  • Decide the rendering model first; it sets your JavaScript baseline and therefore your Core Web Vitals ceiling.
  • Hugo wins raw build speed decisively, but that only matters once you're into the thousands of pages.
  • Eleventy and Hugo ship zero JavaScript by default; Astro ships none until you hydrate an island; a Next.js export carries React on every interactive route.
  • Verify a cold build finishes inside your CI window, and confirm the ecosystem you depend on is actively maintained, before you commit.

FAQ

What is the single most important factor when choosing an SSG for production?

Whether a cold full build finishes inside your CI window. Build speed and incremental support decide whether you can publish reliably at all, and they get worse as the site grows. Authoring experience and ecosystem matter, but a build that times out blocks every release regardless of how nice the framework feels.

Which static site generator ships the least JavaScript?

Eleventy and Hugo ship none by default because they are template-only and have no client runtime. Astro ships zero JavaScript until you hydrate an island. A Next.js static export ships React on every interactive route, so it carries the heaviest baseline of the five.

Is Hugo always the right choice because it builds fastest?

No. Hugo wins raw build speed by a wide margin, but speed only dominates once a repository runs into the thousands of pages. For a few hundred pages the difference is seconds, so authoring experience, ecosystem, and the language your team can debug usually matter more than the benchmark.

How do I decide between Astro and Eleventy for a documentation site?

Choose Astro when you want a component model and the occasional interactive island with type-safe content collections. Choose Eleventy when you want the leanest possible Markdown-to-HTML pipeline with zero default JavaScript and minimal machinery to maintain. Both produce fast static docs.

Does framework choice affect Core Web Vitals?

Yes, mostly through how much JavaScript ships. Template-only generators and island-based ones give you the best INP baseline because little or no client code runs. A full-hydration export can still score well, but you pay for the framework runtime on every interactive page and have to budget it carefully.

Should I pick the framework first or the host first?

Pick the framework against your team and content constraints first, then confirm your target host supports its build and caching model. Every major host runs every major generator, so the framework decision rarely eliminates a host, but a host's incremental-build and preview-deploy support can break a tie.