When we started building our website with React, our primary focus was developer experience and frontend flexibility. As the content section grew, we began paying closer attention to SEO, indexing, and how search engines were seeing our pages.
While modern search engines can render JavaScript, relying entirely on Client-Side Rendering (CSR) introduces additional complexity. Blog content, metadata, and page content are not immediately available in the initial HTML response, which can affect crawl efficiency and content discoverability. Google itself explains many of these challenges in its JavaScript SEO guidelines.
This led us to evaluate different rendering strategies and eventually migrate from React to Next.js.
Understanding the Rendering Options
| Strategy | How It Works | Best For |
|---|---|---|
| CSR | Browser renders everything after loading JavaScript | Dashboards, internal tools |
| SSR | Server generates HTML on every request | Dynamic content |
| SSG | Pages generated during build time | Blogs, marketing sites |
| ISR | Static pages regenerated periodically | Large content libraries |
For a content-heavy website, SSG provided the best balance between SEO, performance, and infrastructure simplicity.

Our First Attempt: Pre-rendering React
Before migrating frameworks, we experimented with pre-rendering.
The workflow was:
React App
↓
Build
↓
Puppeteer
↓
Generate HTML Snapshots
↓
Deploy
Using Puppeteer, we generated static HTML versions of important pages so crawlers could access fully rendered content.
Pros
- Better SEO than pure CSR
- No framework migration required
- Improved metadata visibility
Cons
- Longer build times
- Additional maintenance
- Difficult to scale for growing content
It worked, but we were essentially building features that modern frameworks already provide.
Why We Chose Next.js
Next.js gave us built-in support for:
- Static Site Generation (SSG)
- Server-Side Rendering (SSR)
- Dynamic metadata
- File-based routing
- Image optimization
The framework also provides a modern metadata API for generating SEO-friendly titles, descriptions, Open Graph tags, and social previews, which is documented in the official Next.js Metadata documentation.
More importantly, it allowed us to choose the rendering strategy per page instead of applying one solution everywhere.
For example:
- Dashboard pages → CSR
- Content pages → SSG
- Dynamic pages → SSR
The Blog Content Challenge
Our content was managed in WordPress and updated regularly.
Fetching content at runtime would create a dependency on the WordPress API for every request. Instead, we chose a static content pipeline.
WordPress
↓
GitHub Actions
↓
Generate JSON Snapshot
↓
Commit Changes
↓
Next.js Build
↓
Deploy
A scheduled GitHub Actions workflow fetches the latest content, generates JSON snapshots, and triggers a deployment. During the build process, Next.js statically generates the blog pages.
This gave us:
- Fully rendered HTML for crawlers
- Faster page loads
- No runtime CMS dependency
- Predictable deployments
If you’re interested in similar engineering topics, you can also explore our other articles on the Blog page.
Lessons Learned
A few key takeaways from this migration:
- React isn’t bad for SEO; rendering strategy matters more than the framework.
- Pre-rendering can be a good transitional solution.
- Not every page needs SSR.
- SSG is often the best choice for blogs and content-heavy websites.
- Separating content updates from runtime requests improves reliability.
Final Thoughts
Our SEO journey wasn’t a direct React-to-Next.js migration.
It evolved through multiple stages:
React CSR
↓
Puppeteer Pre-rendering
↓
Next.js Migration
↓
SSG Content Pipeline
Each step solved a specific problem and helped us build a faster, more SEO-friendly architecture.
The biggest lesson was simple: choose the rendering strategy based on the page’s requirements, not the popularity of the framework.

