For a long time I used React for everything. It worked. Until it wasn't enough.
There were projects where React solved exactly what I needed. Dynamic interfaces, manageable state, reusable components. The workflow was comfortable and I knew it well.
But then came projects where those same patterns started generating friction. SEO was a constant headache. Load times weren't what they should be. The setup before writing any useful code kept feeling longer and longer.
When I started working with Next.js day to day, a lot of that friction simply disappeared. Not because Next.js is better — but because it answers questions React never set out to answer.
What is each one?
React
React is a JavaScript library for building user interfaces. It's not a complete framework — it's just the view layer. It gives you components, a state system, and rendering. Everything else — routing, data fetching, optimization, folder structure — is up to you.
That flexibility is its greatest strength. And also its biggest trap.
Next.js
Next.js is a framework built on top of React. It takes React as its foundation and adds everything it's missing: file-based routing, server-side rendering, static generation, image optimization, API routes, and much more.
It doesn't replace React — it extends it. Everything you know about React works in Next.js.
The differences you feel most day to day
Routing
With plain React you need to install and configure React Router manually:
// React — manual route configuration
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/blog/:id" element={<BlogPost />} />
</Routes>
</BrowserRouter>
);
}
With Next.js you simply create files inside the app/ or pages/ folder and the routes exist automatically:
app/
├── page.tsx → /
├── about/
│ └── page.tsx → /about
└── blog/
└── [id]/
└── page.tsx → /blog/:id
No configuration. No extra imports. The file system is the router.
Rendering
This is the most important difference for real applications.
React (CSR — Client Side Rendering):
The server sends a nearly empty HTML. The browser downloads the JavaScript, executes it, and only then renders the interface. The user sees a blank screen in the meantime.
// React — data loads on the client
function BlogPost({ id }) {
const [post, setPost] = useState(null);
useEffect(() => {
fetch(`/api/posts/${id}`)
.then(res => res.json())
.then(data => setPost(data));
}, [id]);
if (!post) return <div>Loading...</div>;
return <article>{post.content}</article>;
}
Next.js (SSR / SSG):
The server generates the complete HTML before sending it to the browser. The user sees real content from the very first moment.
// Next.js — data is fetched on the server
async function BlogPost({ params }: { params: { id: string } }) {
const post = await fetch(`https://api.example.com/posts/${params.id}`);
const data = await post.json();
return <article>{data.content}</article>;
}
No loading state. No blank screen. No useEffect for data that should have been there from the start.
SEO
This is critical for any project that needs to be found on Google.
With plain React, search engines receive an empty HTML and have to wait for JavaScript to execute before they can see any content. Many crawlers don't wait — they simply index an empty page.
With Next.js, the complete HTML reaches the crawler on the first request. The content is there, ready to be indexed.
// Next.js — SEO metadata directly in the component
export const metadata = {
title: 'My Article | Blog',
description: 'A clear description for search engines',
openGraph: {
title: 'My Article',
images: ['/og-image.jpg'],
},
};
Head-to-head comparison
| Criteria | React | Next.js |
|---|---|---|
| Type | UI Library | Full-stack framework |
| Routing | Manual (React Router) | Automatic (file-based) |
| Rendering | Client only (CSR) | CSR, SSR, SSG, ISR |
| SEO | Hard without extra setup | Native and optimized |
| API Routes | Not included | Included |
| Image optimization | Manual | Automatic with <Image>
|
| Learning curve | Low | Medium (requires knowing React first) |
| Structure flexibility | Total | Opinionated |
| Best for | SPAs, internal dashboards | Public sites, e-commerce, blogs, apps |
Next.js rendering modes
One of the reasons Next.js stands out is that it doesn't force you to pick a single rendering mode. You can mix them within the same project depending on what each page needs.
SSG — Static Site Generation
HTML is generated at build time. Ideal for content that doesn't change often: blogs, landings, documentation.
// This page is generated once at build time
async function BlogIndex() {
const posts = await getPosts();
return <PostList posts={posts} />;
}
SSR — Server Side Rendering
HTML is generated on every request. Ideal for personalized or constantly changing content.
// This page is regenerated on every visit
async function Dashboard() {
const data = await getUserDashboard();
return <DashboardView data={data} />;
}
ISR — Incremental Static Regeneration
The perfect mix: generated statically but revalidated at intervals.
// Revalidates every 60 seconds in the background
export const revalidate = 60;
async function ProductPage({ params }) {
const product = await getProduct(params.id);
return <ProductView product={product} />;
}
When to use plain React
React without Next.js is still the right choice in several scenarios:
- Internal dashboards that don't need SEO and have authenticated users
- Single page applications where content is completely dynamic
- Projects where a separate backend already exists and you only need the UI layer
- Teams that want full control over every architectural decision
- Quick prototypes where minimal setup is the priority
When to use Next.js
Next.js is the best option when:
- The project needs real SEO — blogs, e-commerce, marketing pages
- You want better performance from the very first render
- You need API routes without setting up a separate server
- The project includes both public pages and authenticated areas
- You want image, font, and script optimization without manual work
- You're building something that will grow and you need a solid foundation
What nobody tells you
Next.js has a real learning curve. The App Router, Server Components, the difference between 'use client' and 'use server', hydration, nested layouts — these are concepts that take time to fully understand.
Going straight to Next.js without a solid React foundation can be confusing. Order matters:
1. Understand React → components, props, state, hooks
2. Build something real with plain React
3. Hit its limitations (SEO, performance, routing)
4. Then Next.js will make complete sense
It's not that one is better than the other in absolute terms. It's that Next.js solves problems that plain React doesn't address by design.
Conclusion
React and Next.js are not rivals. Next.js is React with specific superpowers: server-side rendering, automatic routing, native SEO, and an architecture built for production from day one.
If you're building an internal SPA or a prototype, plain React is enough. If you're building something public that needs to load fast, rank in search engines, and scale — Next.js is the right call.
The question isn't which one is better. The question is what problem are you solving.
"React gives you the pieces. Next.js gives you the board where they fit."
Are you using React, Next.js, or both in your projects? I'd love to read your experience in the comments.

Top comments (1)
@geampiere the dividing line I draw is "do users land on URLs". If yes, Next.js — SSR/SSG + metadata + sitemaps come for free. If the product is auth-gated SPA where every URL needs the user logged in, Vite+React is faster to iterate on. We actually run both: the marketing site is Next.js, the in-app dashboard is Vite+React. Same React, different pain points.