Skip to content

React ​

headless-prerender works with any client-rendered React app - Create React App, Vite, or a custom bundle - using React Router or any other router. No changes to your components are required.

When you need it ​

Your app renders into an empty root element:

html
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>

Crawlers that do not run JavaScript see only that empty div. Prerendering gives them the rendered output instead.

Setup ​

  1. Deploy your built app as usual (static files behind a CDN or web server).

  2. Run the prerender service:

    bash
    headless-prerender serve \
      --port 3000 \
      --allow-origin https://www.example.com \
      --cache-ttl 300000
  3. Route bot traffic to it.

Signalling "ready to snapshot" ​

By default the renderer waits for the network to go idle (networkidle2). If your app finishes rendering after that - lazy routes, delayed data - give it more time:

bash
headless-prerender serve --extra-wait 500

For a precise signal, expose a global flag once your app has painted its content, and render with a longer wait:

tsx
useEffect(() => {
  // after the route's data has loaded
  (window as any).__PRERENDER_READY__ = true;
}, [dataLoaded]);

Then poll for it in a small wrapper around the library:

ts
import { Renderer } from "headless-prerender";

const renderer = new Renderer({ waitUntil: "networkidle0", extraWaitMs: 200 });
const { html } = await renderer.render("https://www.example.com/page");

(There is no built-in "wait for selector" option; use extraWaitMs or a proxy that retries.)

Correct status codes for 404s ​

An SPA usually serves index.html with a 200 for unknown routes. So bots can see a real 404, render a <meta name="prerender-status-code"> tag in your not-found route and have your proxy read it, or serve those routes from the server with the right status.

tsx
function NotFound() {
  return (
    <>
      <meta name="prerender-status-code" content="404" />
      <h1>Page not found</h1>
    </>
  );
}

Meta tags and social previews ​

Set per-route <title> and <meta property="og:*"> tags with react-helmet-async or React 19 document metadata. The prerender captures them into the static HTML, so social scrapers get correct previews.

Checklist ​

  • [ ] --allow-origin set to your site
  • [ ] --extra-wait tuned so late content is captured
  • [ ] Per-route <title> / Open Graph tags rendered client-side
  • [ ] 404 routes distinguishable (status-code meta tag or server route)
  • [ ] Caching enabled

Released under the MIT License.