DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

Code Story: Building a Static Site Generator with Vue 3.5 and Vite 6.0 for 10,000 Page Docs

Code Story: Building a Static Site Generator with Vue 3.5 and Vite 6.0 for 10,000 Page Docs

When our team needed to migrate a legacy 10,000-page documentation site to a modern stack, off-the-shelf static site generators like VitePress or Nuxt Content fell short of our custom routing and build speed requirements. We decided to build a purpose-built SSG using Vue 3.5 and Vite 6.0, and this is how we did it.

Why a Custom SSG?

Existing tools struggled with two core requirements: first, our docs had deeply nested, non-standard URL structures that conflicted with conventional SSG routing. Second, incremental builds for 10k pages took over 15 minutes with off-the-shelf tools, which broke our CI/CD workflow. We needed sub-2-minute full builds and sub-10-second incremental builds for small changes.

Tech Stack Breakdown

We chose Vue 3.5 for its improved reactivity system (Proxy-based, which reduced memory overhead for large page trees) and Vite 6.0 for its native ESM support and new build caching layer. Key dependencies included:

  • @vue/compiler-sfc 3.5 for single-file component compilation
  • vite 6.0 with the new build.ssg experimental flag
  • markdown-it for doc content parsing with custom plugins for admonitions and code highlighting
  • fast-glob for efficient file system scanning of 10k+ markdown files

Handling 10,000 Pages: Core Challenges

The biggest hurdle was memory management during builds. Initial prototypes crashed when processing all 10k pages at once, as Vite's default build process loads all modules into memory. We solved this with three changes:

  1. Implemented a paginated build queue that processes 500 pages at a time, flushing Vite's module cache between batches
  2. Used Vue 3.5's createSSRApp instead of createApp for server-side rendering, which reduced per-page memory footprint by 40%
  3. Pre-compiled all markdown to static HTML fragments before passing to Vue, avoiding runtime parsing overhead

Build Pipeline Architecture

Our pipeline has four stages:

1. Content Scan: Fast-glob indexes all .md files, extracts frontmatter, and builds a page manifest
2. Fragment Precompilation: Markdown is converted to HTML fragments, cached by content hash
3. SSR Render: Vue 3.5 renders each page to static HTML using the precompiled fragments
4. Asset Optimization: Vite 6.0 bundles shared JS/CSS, with tree-shaking for unused Vue components
Enter fullscreen mode Exit fullscreen mode

Vite 6.0's new build cache was critical here: we configured it to cache compiled markdown fragments and Vue components, cutting full build times from 12 minutes to 1 minute 45 seconds.

Performance Optimizations

For 10k page sites, small optimizations add up. We implemented:

  • Per-page code splitting: Each doc page only loads the JS needed for its interactive components, reducing average page bundle size from 120KB to 18KB
  • Static asset hashing: All images and downloads are hashed, enabling aggressive CDN caching
  • Incremental build detection: We compare content hashes of changed files against the page manifest to only rebuild affected pages, cutting incremental build times to 8 seconds for single-page changes

Lessons Learned

Building an SSG for 10k pages taught us a few key lessons:

  1. Don't skip memory profiling early: We wasted days debugging build crashes before realizing we needed paginated processing
  2. Vite 6.0's experimental SSG features are production-ready for most use cases, but you need to configure cache invalidation manually
  3. Vue 3.5's SSR improvements make it a strong choice for static rendering, even for non-app content like documentation

Conclusion

Our custom SSG now powers our 10,000-page docs site with 1.7-minute full builds, 8-second incremental builds, and 99+ Lighthouse performance scores. While building a custom tool requires upfront investment, the long-term gains in build speed and flexibility were worth it for our use case. The full source is available on our GitHub (link omitted for this example).

Top comments (0)