Skip to content

Start: prerender.concurrency has no effect on CPU-bound pages (all pages render one at a time in a single preview server) #8483

Description

@R4ULtv

Which project does this relate to?

Start

Describe the bug

prerender.concurrency suggests pages are rendered in parallel, but prerendering sends every page to one preview server (startPreviewServer in start-plugin-core/src/vite/prerender.ts). That server runs SSR in a single JavaScript thread, so concurrency only overlaps I/O waits; the rendering work of each page still happens one page at a time. For sites with many CPU-bound pages, build time grows linearly with page count, and raising concurrency changes nothing.

This isn't adapter-specific. The reproducer uses plain Start with the default Node preview server. With @cloudflare/vite-plugin the preview is one workerd process running one isolate, so the effect is the same.

Real-world impact: in a production app with ~19k prerendered pages (CPU-bound renders from bundled static data, no network I/O), prerendering takes ~110–150s locally at concurrency 1, 2 or 4 alike. In CI (Cloudflare Workers Builds, 4 vCPU) the full build went from ~1 min to ~5 min once all pages were prerendered.

As an experiment I split the same pages across 4 separate preview processes (N workerd instances started against the built dist/server). The output matched the built-in prerenderer's, except for the dehydration timestamp, and prerendering dropped to ~45s, scaling with process count (1 → 2 → 4) up to the machine's performance cores. I'd rather not maintain that workaround, since it depends on internals and unstable APIs, so an official option would be great.

Complete minimal reproducer

https://github.com/R4ULtv/tanstack-start-prerender-concurrency

Steps to Reproduce the Bug

  1. git clone https://github.com/R4ULtv/tanstack-start-prerender-concurrency && cd tanstack-start-prerender-concurrency
  2. npm install
  3. npm run bench. This runs vite build with prerender.concurrency set to 1, 4 and the machine's core count, and prints the time of each build. The app has one route, /page/$id, whose loader does ~15 ms of synchronous CPU work, with 1,000 pages prerendered (PAGE_COUNT to change).

Result on an Apple M-series Mac (10 logical cores, Node 24):

concurrency build time
1 21.3s
4 20.4s
10 19.6s

Expected behavior

With concurrency > 1 on a multi-core machine, prerendering CPU-bound pages should get faster. For example:

  • an option such as prerender.workers: number that starts N preview servers (worker threads or child processes) and spreads the page queue across them; or, at least,
  • documentation stating that concurrency only overlaps I/O and doesn't parallelize rendering.

Screenshots or Videos

No response

Platform

  • Router / Start Version: @tanstack/react-start 1.168.57, @tanstack/react-router 1.170.38
  • OS: macOS 15 (Darwin 24.6.0), Apple M-series (4 performance + 6 efficiency cores)
  • Browser: N/A (build-time issue)
  • Browser Version: N/A
  • Bundler: vite
  • Bundler Version: 8.3.0

Additional context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions