Skip to content
Now accepting Q2 projects — limited slots available. Get started →
Capability

Your Documentation Site Takes 11 Minutes to Build. You've Outgrown Gatsby.

If you're a platform team shipping docs across 12 languages, you know the CI queue is your bottleneck -- Hugo builds 10,000 pages in 8 seconds.

Stack
HugoGo TemplatesTailwind CSSAlpine.jsNetlifyCloudflare Pages

Hugo: the fastest static site generator

Build time is boring until it isn't. When your CI pipeline is queuing 11-minute builds every time a technical writer fixes a typo, it becomes the thing your whole team talks about. That's the point where Hugo stops being a curiosity and starts being the obvious answer.

Hugo builds at roughly 1ms per page. In practice, a 10,000-page documentation site builds in around 8 seconds. Compare that to Gatsby, where the same site can take 8-12 minutes once you factor in GraphQL layer compilation and image processing. Next.js isn't really designed for pure content builds, but if you've tried using it for a 5,000-page docs site, you'll have felt the overhead -- the Node.js startup cost, the incremental cache misses, the memory footprint that creeps past 4GB on a standard CI runner.

Jekyll was the original static site generator that Hugo dethroned. It's not slow exactly, but at 1,000 pages Jekyll starts to feel it. At 5,000 pages you're looking at 2-4 minutes on a modern machine. At 10,000 pages, Jekyll genuinely struggles.

Hugo doesn't struggle. It's written in Go, ships as a single binary, and parallelises page rendering across all available CPU cores. There's no Node.js runtime to cold-start, no npm install step that pulls in 847 packages. You run one binary, it reads your markdown, and the output is sitting in /public before the CI log has finished scrolling.

The real kicker is that this speed holds as your site grows. Linear scaling -- roughly -- means a 50,000-page site built on Hugo still finishes in under a minute. That's not a number you'll see from any JavaScript-based generator at that content volume.

What we build with Hugo

Documentation sites are the obvious fit, and honestly the use case where Hugo earns its reputation fastest. If you're running a platform with SDK references, API guides, changelog pages, and conceptual docs -- all versioned, all in multiple languages -- Hugo's content architecture handles it without custom plugins or workarounds.

Documentation sites are where we spend most of our Hugo time at Social Animal. We've built docs platforms for developer tools, fintech APIs, and internal knowledge bases. Hugo's shortcodes let technical writers embed custom UI components -- tabbed code blocks, callout boxes, versioned content warnings -- without touching a template file.

Multi-language marketing sites are the second major use case. Hugo's multilingual config scales from 2 to 50 languages in the same config.toml -- you define each language as a named block, set URL prefixes or separate domains, and Hugo handles the rest. i18n string files slot in alongside content. We've shipped sites with 18 active locales where the build still completes in under 30 seconds.

Technical content hubs -- the kind of site that publishes 40 blog posts a month, maintains a resource library of 3,000 pieces, and needs taxonomy pages for every tag combination -- are where Hugo's built-in taxonomies earn their keep. No plugin. No custom API. Just declare your taxonomy in config and Hugo generates the archive, tag, and category pages automatically.

One concrete example: we migrated a SaaS documentation site from Gatsby to Hugo last year. Pre-migration build time was 14 minutes on a standard GitHub Actions runner. Post-migration: 22 seconds. Same content, same hosting on Cloudflare Pages, dramatically faster deploys.

Hugo's trade-offs

Let's be honest about the rough edges, because there are some.

Go templates are the biggest adjustment. If your team lives in React or Vue, Hugo's template syntax will feel alien at first. There's no JSX, no component props, no familiar .map() and conditional rendering. Instead you get {{ range }}, {{ with }}, and {{ partial }} -- all perfectly logical once you've spent a day with them, but genuinely not what JavaScript developers expect. The learning curve is real. A competent JavaScript developer can probably get productive in Hugo within a week, but it's a week.

There's no component model in the React sense. Hugo has partials and shortcodes, which cover 90% of what components do, but they're not stateful. If your design system is built around Storybook-driven React components that carry local state, you'll feel the gap.

Interactivity requires reaching for vanilla JavaScript, Alpine.js, or HTMX. For most content sites this is fine -- better than fine, actually, because you're shipping far less JavaScript than a React-based site would. But if you need rich client-side filtering, interactive data visualisations, or anything that feels like a web app, Hugo gets awkward. It's not the tool for that.

So: content-heavy, performance-first, multilingual, mostly read-only? Hugo wins. Interactive product with complex UI state? Next.js or SvelteKit.

When Hugo beats Next.js (and when it doesn't)

Here's the honest decision framework we use when scoping a new project.

Hugo is the right call when:

  • You're building a documentation site with 500+ pages and deploys need to be fast enough that writers don't wait. A 30-second build means you can deploy on every commit.
  • Your site needs to support 5+ languages and you don't want to manage a translation plugin or a custom i18n layer. Hugo's multilingual config just works.
  • You're hosting on Cloudflare Pages, Netlify, or GitHub Pages and want zero server-side infrastructure. Hugo's output is pure static HTML -- no runtime, no serverless functions required.
  • Your team writes content in markdown and wants to stay there. Hugo's content architecture is built around the filesystem, and writers with no development background can contribute.

Next.js is the right call when:

  • You need pages that fetch personalised or real-time data per user. Hugo has no server runtime. There's nothing to run a database query.
  • Your site is closer to a web application than a content site -- user authentication, form state, client-side routing between dynamic views.
  • Your existing design system is React-based and rebuilding it in Hugo partials would cost more than Next.js's build overhead is worth.
  • You need Incremental Static Regeneration -- pages that rebuild on a schedule or on-demand without a full site rebuild.

The grey area is the mid-size marketing site: 100-400 pages, some personalisation, mostly content. Either tool works. But we've found that teams consistently underestimate how often they'll need a fast full rebuild -- a global nav change, a banner announcement, a legal update across 8 locales -- and for those teams, Hugo's build speed is the tie-breaker.

Hugo content architecture we default to

Pretty straightforward in principle, genuinely powerful in practice. Here's the directory structure we start from on most Hugo projects:

content/
  en/
    docs/
    blog/
    changelog/
  de/
  fr/
layouts/
  _default/
    baseof.html
    single.html
    list.html
  partials/
    head.html
    nav.html
    footer.html
  shortcodes/
assets/
  css/
  js/
i18n/
  en.yaml
  de.yaml
  fr.yaml
config/
  _default/
    config.toml
    languages.toml
    params.toml
    taxonomies.toml

Content lives under language directories, which maps directly to Hugo's multilingual config. Each language block in languages.toml sets the language code, the base URL, and the content directory. Adding a new language is genuinely just adding one config block and a new i18n/ file -- we've scaled this pattern to 50 languages on a large SaaS marketing site without the config becoming unmanageable.

Taxonomies are declared in taxonomies.toml -- typically category, tag, and for documentation sites, product or version. Hugo generates list pages for every taxonomy term automatically, which means your /blog/tag/api-design/ pages exist with zero extra work.

For content types that need custom rendering, render hooks in layouts/_default/_markup/ let you override how Hugo processes specific markdown elements -- images, links, headings -- without touching the content files themselves.

Hugo theming without the Go template pain

Go templates look intimidating until you understand three things: baseof.html, blocks, and partials. Once those click, you can build a full theming system pretty cleanly.

baseof.html is your outer shell -- the HTML document wrapper that every page inherits. You define named blocks inside it ({{ block "main" . }}{{ end }}), and individual layout files fill those blocks. single.html fills the main block for single content pages. list.html fills it for archive and taxonomy pages. And you can override at the content type level -- a layouts/docs/single.html overrides the default only for content in the docs directory.

Partials are the component system. They're synchronous functions that take a context argument and return HTML. {{ partial "nav.html" . }} renders your navigation. {{ partial "card.html" . }} renders a content card. You can pass arbitrary data with {{ partial "card.html" (dict "title" .Title "image" .Params.image) }}. In practice this covers most of what React components do for a content site.

Shortcodes are partials that work inside markdown content files. A layouts/shortcodes/callout.html becomes {{< callout type="warning" >}} inside any content file. Technical writers get a clean, writable syntax. You maintain the HTML in one place. It's a clean separation that works well in practice.

Hugo's asset pipeline -- via resources.Get, css.Sass, and js.Build -- handles SCSS compilation and JS bundling natively since Hugo 0.80. No Webpack config. No extra build step.

Migrating to Hugo from WordPress, Gatsby, or Jekyll

From WordPress: This is the most common migration we handle. WordPress exports to XML; there are several tools that convert WP XML to Hugo markdown with reasonable fidelity, though you'll spend time cleaning up shortcode conversions and image paths. The real work is usually the template layer -- rewriting PHP templates as Hugo layouts -- and the redirects file, which needs to preserve every existing URL. We've run migrations for sites with 4,000+ posts and the build time improvement alone -- from 45-second WP page loads on a poorly cached server to sub-100ms Cloudflare Pages delivery -- justifies the project cost immediately.

From Gatsby: If you're on Gatsby, you're probably migrating because the build times are unsustainable or because Gatsby's GraphQL layer has become a maintenance burden. Content migration is clean: Gatsby's markdown frontmatter is largely compatible with Hugo's. The harder part is the component layer. If your Gatsby site uses a rich React component library for the theme, you're rewriting that in Hugo partials. Budget realistically for that -- it's not a one-week job for a complex theme, but the output is leaner and your build drops from 10+ minutes to under 30 seconds.

From Jekyll: Honestly the easiest migration of the three. Jekyll and Hugo share a lot of DNA -- both use markdown, both use front matter, both have liquid/Go template concepts that map roughly to each other. Content files often migrate with minimal changes. The main friction is Liquid tags vs Hugo shortcodes, and the fact that Jekyll plugins (particularly jekyll-feed, jekyll-seo-tag, and jekyll-paginate) need to be replaced with Hugo equivalents -- which mostly exist as built-in features rather than plugins. Build time improvement is significant: a 3,000-page Jekyll site that builds in 4 minutes rebuilds in Hugo in about 5 seconds.

Social Animal

Need help with your documentation site takes 11 minutes to build. you've outgrown gatsby.?

Get a free quote
FAQ

Common questions

When does Hugo beat Next.js for content-heavy sites?

Hugo wins whenever your site is primarily Markdown content with minimal server-side logic. If you're running a blog, docs portal, or marketing site with 500+ pages, Hugo's static output loads faster, costs less to host, and has zero Node.js dependency bloat. Next.js makes sense when you need API routes, authenticated dashboards, or heavy client-side state. Pick Next.js for apps; pick Hugo for content. Mixing them up wastes everyone's time and budget.

Is Hugo's Go template language hard to learn?

Honestly, yes -- at first. Go templates use a pipe syntax like {{ .Title | upper }} that trips up developers coming from Liquid or Nunjucks. Expect roughly 3-5 days before it clicks. The biggest stumbling block is variable scoping inside range loops, where you lose access to outer context. Once you learn the $ prefix trick to grab the root context, things move quickly. The Hugo docs are solid, and the Discord community answers template questions within hours.

How does Hugo compare to Astro for documentation sites?

Astro wins on component flexibility -- you can drop in React or Vue islands where you need them. Hugo wins on pure build speed and operational simplicity. For docs sites under 2,000 pages without interactive code playgrounds, Hugo with a theme like Docsy or Geekdoc ships faster and requires zero JavaScript bundler config. If your docs need live code execution, syntax-aware search widgets, or component demos, Astro's island architecture earns its extra complexity. Our default recommendation for straightforward docs is still Hugo.

How does Hugo handle multilingual sites at scale?

Really well, actually. Hugo's built-in i18n system supports content directories per language, string translation files in TOML or YAML, and automatic hreflang tag generation. We've shipped 12-language sites with 8,000+ pages per locale, and Hugo handles it without plugins or workarounds. You define languages in hugo.toml, structure content under /content/en/, /content/fr/, and so on, and Hugo manages routing automatically. Build times do grow linearly per locale, so a 5-language site takes roughly 5x longer -- still under 60 seconds for most projects.

Can I add interactive components to a Hugo site?

Yes, but you're doing it manually rather than through a framework. We typically reach for Alpine.js for lightweight interactions like accordions, tabs, and dropdowns -- it adds under 15kb and lives in your HTML as attributes. For heavier components like search (Pagefind works great here) or contact forms, we embed standalone scripts or use services like Netlify Forms. Hugo doesn't care what JavaScript you add. It just won't manage bundling for you, so pairing it with a simple Vite config handles that cleanly.

What's Hugo's build time on a 10,000-page site?

Typically 8-25 seconds on a modern laptop, depending on image processing load. Hugo's own benchmark puts it at under 1ms per page for pure content. The real slowdown is Hugo Pipes image resizing -- processing 10,000 unique images can push builds past 2 minutes on first run. After that, Hugo's persistent disk cache kicks in and subsequent builds drop back under 30 seconds. For CI/CD pipelines, caching the resources/_gen folder between runs is the single biggest speed improvement you can make.

What are my Hugo hosting options and what do they cost?

Hugo outputs plain static files, so your options are wide open. Netlify and Cloudflare Pages both offer free tiers that handle most small-to-medium sites without a credit card. Cloudflare Pages has no bandwidth limits on its free plan, which makes it our default for client sites. AWS S3 plus CloudFront runs roughly $1-5/month for most traffic levels. GitHub Pages is free but limits you to public repos on the free plan. Vercel works fine but is primarily optimized for Next.js, so you're paying for features you won't use.

Migrating from Gatsby to Hugo: what actually breaks?

Three things consistently cause pain. First, GraphQL data sourcing -- Hugo uses local files and TOML/YAML front matter instead, so any Gatsby source plugins need manual replacement. Second, React components don't transfer; you're rewriting UI in Go templates or swapping in Alpine.js. Third, gatsby-image's automatic responsive images need to be rebuilt using Hugo's built-in image processing functions. Plan for 2-4 weeks on a serious Gatsby migration. The upside: build times drop from 4-8 minutes to under 30 seconds, and you eliminate roughly 800MB of node_modules.

Ready to get started?

Free consultation. No commitment. Just an honest conversation about your project.

Book a free call →
Get in touch

Let's build
something together.

Whether it's a migration, a new build, or an SEO challenge — the Social Animal team would love to hear from you.

Get in touch →