Jul 13, 2026Guide

Overcoming React SPA SEO Limitations with react-snap and Hybrid Pipelines

Discover how to solve crawler blank screen issues and infinite redirect loops in React SPAs by implementing SSG with react-snap and a Cloudflare hybrid pipeline.

✍️Written by SungGeun Kim

The Limits of Simple React SPAs and SEO Pitfalls

Single Page Applications (SPAs) are fantastic for user experience, but they come with a critical flaw: until JavaScript is executed, they serve a blank HTML page. This prevents Googlebot and other crawlers from properly indexing the content. For projects aiming for Google AdSense approval or organic traffic acquisition, solving this is absolutely mandatory.

Furthermore, overly complex server-side rendering setups or aggressive redirection strategies implemented for crawlers often lead to infinite redirect loops. Routing logic conflicts with server rendering logic, confusing the browser and causing endless page reloads.

The Solution: Introducing SSG via react-snap

Instead of embarking on a massive Next.js migration, I implemented a lightweight alternative: react-snap. During the build process, react-snap leverages Puppeteer to spawn a background browser, traverses each route, and saves the rendered output as static HTML.

Guarding Against Build Errors with Dummy Configs

The most challenging aspect of integrating react-snap was mitigating build-time errors. For instance, Firebase initialization or browser-specific APIs would often crash the build process.

// Example of a guard condition using isBuildEnv
const isBuildEnv = navigator.userAgent === 'ReactSnap';

if (!isBuildEnv) {
  // Logic that should only run in a real browser (e.g., onAuthStateChanged)
  initFirebase();
}

By detecting isBuildEnv, I prevented unnecessary third-party scripts from executing during the static rendering phase, significantly stabilizing the build.

Overcoming Cloudflare Pages Limitations: The Hybrid Pipeline

Even after successfully generating static HTML, another hurdle remained. The Cloudflare Pages v2 runtime does not support Puppeteer (a core dependency of react-snap), making direct builds on the platform impossible. Moreover, deploying directly via the wrangler CLI led to domain alias lock issues, disrupting the pipeline.

Implementing a Release Branch Pattern

To resolve this, I designed a hybrid deployment pipeline.

  1. Source Branch (main): All development code is pushed here.
  2. Build Engine (GitHub Actions): A push to main triggers GitHub Actions, running npm run build. This generates the static HTML via react-snap.
  3. Ad Script Purge: A cleanup-ads.js script runs post-build, using string-based (indexOf) matching to surgically remove unnecessary localhost-environment ad scripts from minified files.
  4. Backend Merging: The API server's functions directory is copied into the completed dist folder. (cp -r functions ./dist/)
  5. Target Branch (production-build): Only the final contents of the dist folder are force-pushed to this branch.
  6. Final Deployment: Cloudflare Pages' Git Integration detects the update on production-build and instantly deploys the live server.

Thanks to this architecture, I established a robust infrastructure that ensures perfect SEO while seamlessly deploying the backend Functions from a single repository. If you operate an SPA-based site and face similar challenges, I highly recommend this hybrid pipeline approach.