# Next.js Explained: Why It Became the Default React Framework

> *"If React is so popular, why was Next.js created?"*

That's exactly the right question to start with. React is one of the most beloved JavaScript libraries ever built. Millions of developers use it daily. Companies stake their entire products on it. So when a framework called Next.js came along and claimed to make React "better," it was fair to be skeptical.

But Next.js didn't just survive that skepticism — it became the industry default for React applications. Used by Netflix, TikTok, Twitch, Notion, and Vercel, it has quietly redefined what "building with React" looks like in production.

This post explains exactly why. Not by marketing features, but by starting where Next.js started: at the real problems React apps face in the wild.

* * *

## Part 1: The Problems That Created Next.js

### React Is a Library, Not a Framework

React is extraordinary at one thing: building user interfaces. It gives you components, state, props, hooks, and a virtual DOM. That's it. Everything else — routing, data fetching, server communication, file structure, deployment — is up to you.

This freedom is intentional and liberating, but it comes at a cost. In production, that cost compounds:

*   Which routing library do you use?
    
*   How do you handle SEO for a JavaScript-rendered page?
    
*   How do you fetch data efficiently at the page level?
    
*   What's your build setup? Webpack? Vite? Something else?
    
*   How do you handle authentication, layouts, error boundaries across pages?
    

Every team answers these questions differently. For small projects, that's fine. For growing teams and serious applications, it creates fragmentation.

### The SEO Problem Was Real

Here's the core issue with a standard React app: when a search engine crawler visits your page, it receives an almost-empty HTML file that looks roughly like this:

```html
<!DOCTYPE html>
<html>
  <head>
    <title>My App</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="bundle.js"></script>
  </body>
</html>
```

The actual content — your products, articles, company description — doesn't exist in this HTML. It only appears *after* the JavaScript bundle downloads, parses, and executes in the browser. For Google Search and other crawlers, that content might as well not exist.

This is called **client-side rendering (CSR)**, and while Google's crawler has improved at handling JavaScript, many crawlers still can't reliably index CSR content. For marketing pages, e-commerce, and content sites, this is unacceptable.

### The Performance Problem Was Expensive

Closely related to the SEO problem is the performance problem. In a pure React app:

1.  The browser downloads a mostly empty HTML file.
    
2.  The browser downloads the JavaScript bundle (often hundreds of kilobytes or more).
    
3.  The browser parses and executes the JavaScript.
    
4.  React renders the UI.
    
5.  The user finally sees something.
    

On a fast laptop with good broadband, this feels instant. On a mid-range phone on a 3G connection in the middle of a country — which describes a large portion of global internet users — this can take 5–10 seconds. The page just shows white until JavaScript finishes loading.

This metric has a name: **First Contentful Paint (FCP)**. And poor FCP doesn't just frustrate users — Google uses it as a ranking signal.

### Growing Complexity

As React applications grow, the absence of structure starts to hurt. Without conventions for organizing files, handling nested layouts, or sharing authentication state across routes, teams end up building their own frameworks — reinventing the wheel inconsistently across codebases.

These real-world problems are precisely what Next.js was built to solve.

* * *

## Part 2: React vs Next.js — What Each One Does

The cleanest way to understand Next.js is this:

**React is a library for building UI.** **Next.js is a framework for building web applications using React.**

React is a tool. Next.js is an opinionated, production-ready platform that picks up where React leaves off.

|  | React | Next.js |
| --- | --- | --- |
| **What it is** | UI library | Full-stack framework |
| **Routing** | Choose your own (React Router, etc.) | Built-in file-based routing |
| **Data fetching** | Choose your own (SWR, React Query, etc.) | Built-in server and client patterns |
| **Rendering** | Client-side by default | CSR, SSR, SSG, ISR — all available |
| **API routes** | Not included | Built-in API routes |
| **Build system** | Configure yourself | Zero-config (built on Turbopack) |
| **Image optimization** | Not included | Built-in `<Image>` component |
| **SEO** | Difficult by default | Metadata API, `<Head>`, sitemap support |
| **Deployment** | Configure for your host | Optimized for Vercel, runs anywhere |

Next.js doesn't replace React — it wraps it. Every component you write in Next.js is still a React component. You still use `useState`, `useEffect`, `useContext`, and all the React primitives you know. What Next.js adds is the structure, the server capabilities, and the production tooling that React alone doesn't provide.

* * *

## Part 3: Understanding Rendering Strategies

This is the most important concept in Next.js, and the one most people misunderstand. Next.js doesn't just do server-side rendering. It gives you *four different rendering strategies* and lets you mix them across different pages of the same application.

### Client-Side Rendering (CSR)

This is standard React behavior. The server sends an empty HTML shell, and the browser downloads JavaScript, runs it, and builds the UI on the client.

**When to use it:** Dashboards, user-specific pages, admin panels — anything behind a login that doesn't need SEO and doesn't have to be publicly crawled.

**Example:** Your analytics dashboard. No one searches for "your Q3 sales report" on Google. Load it entirely in the browser.

### Server-Side Rendering (SSR)

With SSR, every request triggers the server to fetch data, render the page to full HTML, and send that HTML to the browser. The user sees content immediately — before any JavaScript runs.

**When to use it:** Pages with user-specific content that also need to be fast, or pages where data changes so frequently that caching is impractical.

**Example:** A Twitter-like feed showing posts for a specific logged-in user. It changes constantly, it's user-specific, and you still want fast load times.

### Static Site Generation (SSG)

With SSG, pages are rendered at *build time* — when you deploy the application, not when a user visits. The output is plain HTML files that can be served from a CDN at lightning speed.

**When to use it:** Marketing pages, blog posts, documentation — content that doesn't change between deployments.

**Example:** A company's "About Us" page or a technical documentation site. It looks the same for every visitor, every time. Generate it once. Serve it from CDN globally.

### Incremental Static Regeneration (ISR)

ISR is the evolution of SSG. Pages are still pre-rendered statically, but Next.js can regenerate them in the background at a specified interval, or on-demand when content changes.

**When to use it:** E-commerce product pages, news sites, content-heavy applications where you want static performance but reasonably fresh data.

**Example:** An Amazon-like product page. It's essentially the same for every visitor, so you generate it statically. But the price or stock level might change — so you regenerate it every 60 seconds automatically.

### Why Multiple Rendering Strategies?

Because no single rendering approach is optimal for every page of a real application. A production-grade app needs all of them:

*   Your home page → SSG (blazing fast, great for SEO)
    
*   Your product pages → ISR (fresh data, CDN performance)
    
*   Your blog feed → SSR (personalized but server-rendered)
    
*   Your dashboard → CSR (user-specific, behind auth)
    

Next.js lets you use the right tool for each page. Traditional React apps can only use CSR.

* * *

## Part 4: File-Based Routing

One of Next.js's most celebrated features is also one of its simplest to understand: **your folder structure is your routing.**

In a traditional React app with React Router, you write routing configuration manually:

```jsx
// React Router — manual configuration
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/blog/:slug" element={<BlogPost />} />
  <Route path="/products/:id/reviews" element={<ProductReviews />} />
</Routes>
```

In Next.js, you just create files in the `app` folder:

```plaintext
app/
├── page.tsx           → /
├── about/
│   └── page.tsx       → /about
├── blog/
│   └── [slug]/
│       └── page.tsx   → /blog/anything-here
└── products/
    └── [id]/
        └── reviews/
            └── page.tsx → /products/123/reviews
```

That's it. Create a `page.tsx` file inside a folder, and Next.js automatically creates the route. No router configuration. No import. No registration.

Dynamic segments — like a blog post slug or a product ID — use square bracket notation: `[slug]`, `[id]`. Catch-all routes use `[...slug]`. Optional catch-all uses `[[...slug]]`.

This approach has meaningful benefits beyond simplicity:

*   **Co-location** — your route, its data fetching logic, its tests, and its CSS can all live in the same folder.
    
*   **Predictability** — anyone new to the codebase can understand the routing structure by looking at the folder tree.
    
*   **Zero configuration** — no library to install, no config to maintain.
    

* * *

## Part 5: Layouts and Application Structure

A layout is a UI wrapper that persists across pages. Think: the navigation bar, the sidebar, the footer. These elements should appear on every page without remounting and losing their state.

In the App Router (Next.js 13+), layouts are defined by creating a `layout.tsx` file in any folder. Every page inside that folder automatically gets wrapped in that layout.

```plaintext
app/
├── layout.tsx            ← Root layout (applies to everything)
├── page.tsx              ← Home page
└── dashboard/
    ├── layout.tsx        ← Dashboard layout (sidebar, etc.)
    ├── page.tsx          ← /dashboard
    └── settings/
        ├── layout.tsx    ← Settings layout (settings tabs)
        └── page.tsx      ← /dashboard/settings
```

When a user navigates from `/dashboard` to `/dashboard/settings`:

*   The root layout stays mounted (nav bar stays, no flicker)
    
*   The dashboard layout stays mounted (sidebar stays)
    
*   Only the page content swaps out
    

This is called **nested layouts**, and it solves a persistent pain point in React SPAs where entire layout trees would re-render on navigation, causing state loss and visual flicker.

* * *

## Part 6: The App Router

The App Router (introduced in Next.js 13 and stable in Next.js 14) is the modern routing and rendering architecture in Next.js. It replaced the older `pages/` directory approach.

The App Router is not just a new way to organize files — it's a fundamentally different execution model built on React Server Components (more on those below).

Key concepts in the App Router:

*   `page.tsx` — defines the UI for a route
    
*   `layout.tsx` — defines a persistent wrapper for that route and its children
    
*   `loading.tsx` — defines a loading skeleton shown while data fetches
    
*   `error.tsx` — defines an error boundary for that route
    
*   `not-found.tsx` — defines a custom 404 page
    
*   `route.ts` — defines an API endpoint (no UI)
    

Each of these is just a file in the right place. Next.js reads your folder structure and assembles the complete application automatically.

The App Router also introduced **parallel routes** (rendering two pages side-by-side, like a split view) and **intercepting routes** (showing a modal while maintaining the URL of the original page, like Instagram's photo modal). These are advanced patterns that reflect how complex modern UIs actually behave.

* * *

## Part 7: Server Components vs Client Components

This is the conceptual breakthrough of modern Next.js — and the concept that confuses people the most.

### Why Server Components Were Introduced

In a traditional React application, every component runs in the browser. This means every component's logic — including any data fetching, formatting, or business logic — gets bundled into the JavaScript that every user downloads.

This is wasteful. A component that fetches data from a database and renders a list of products doesn't need to run in the browser. It just needs to produce HTML. The browser doesn't need the database query logic. It doesn't need the markdown parsing library. It doesn't need any of that — it just needs the HTML output.

React Server Components let you write components that run *only on the server*, never in the browser.

### Server Components

By default in the App Router, **all components are Server Components**. They can:

*   Fetch data directly from databases, files, or APIs — without an API layer
    
*   Access server-only secrets (API keys, database credentials) without exposing them to the browser
    
*   Be rendered to HTML on the server and sent to the client
    

They cannot:

*   Use `useState`, `useEffect`, or other browser-only hooks
    
*   Access browser APIs (`window`, `document`)
    
*   Handle user events like `onClick`
    

### Client Components

To use browser features, you add `"use client"` to the top of the file. This marks the component as a Client Component — it will be included in the JavaScript bundle and run in the browser.

```tsx
"use client"

import { useState } from "react"

export function SearchBar() {
  const [query, setQuery] = useState("")

  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search..."
    />
  )
}
```

### The Mental Model

Think of your application as a tree of components. Most of that tree can be Server Components — they run on the server, produce HTML, and ship zero JavaScript to the browser. A small number of components — anything interactive — become Client Components.

This means users download dramatically less JavaScript, pages render faster, and server-only code (like database queries) stays completely isolated from the browser.

* * *

## Part 8: Data Fetching in Next.js

Data fetching in Next.js Server Components is refreshingly simple:

```tsx
// app/products/page.tsx — a Server Component
async function ProductsPage() {
  // This runs on the server — no useEffect, no loading state needed
  const products = await fetch("https://api.example.com/products").then(r => r.json())

  return (
    <ul>
      {products.map(p => <li key={p.id}>{p.name}</li>)}
    </ul>
  )
}
```

This is just an `async` function. No `useEffect`. No loading state management. No API route in the middle. The component fetches data as part of its render — on the server — and returns HTML.

Compare this to the traditional React data-fetching pattern:

```jsx
// Traditional React — much more ceremony
function ProductsPage() {
  const [products, setProducts] = useState([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetch("/api/products")
      .then(r => r.json())
      .then(data => {
        setProducts(data)
        setLoading(false)
      })
  }, [])

  if (loading) return <div>Loading...</div>

  return (
    <ul>
      {products.map(p => <li key={p.id}>{p.name}</li>)}
    </ul>
  )
}
```

The traditional approach requires more boilerplate, exposes a loading state to users (white flash), and makes an extra round trip (browser → Next.js API route → external API) that the server-side approach eliminates.

* * *

## Part 9: Performance Benefits

All of these architectural decisions compound into real, measurable performance benefits:

**Faster initial page loads** — SSR and SSG mean users see content almost immediately, rather than waiting for a JavaScript bundle to execute.

**Better SEO** — HTML is fully rendered and crawlable by search engines on the first request.

**Less JavaScript in the browser** — Server Components ship zero JS by default. Client Components are only added to the bundle when needed. The result is a smaller bundle and faster JavaScript execution.

**Improved Core Web Vitals** — Google's ranking signals (LCP, FCP, CLS, INP) all improve with proper rendering strategies and Next.js's built-in optimizations.

**Optimized asset delivery** — Next.js's `<Image>` component automatically resizes images, converts to modern formats (WebP, AVIF), lazy-loads off-screen images, and prevents layout shift. Its `<Font>` component automatically optimizes font loading to eliminate invisible text flash.

* * *

## Part 10: When to Use Next.js

### Reach for Next.js when building:

**Marketing websites and landing pages** — SSG gives you static performance with a great developer experience. Built-in image optimization and metadata API make SEO straightforward.

**SaaS products** — The combination of SSG for marketing pages, SSR for authenticated user pages, and Client Components for interactive UI covers every part of a typical SaaS product naturally.

**E-commerce platforms** — ISR is a game-changer here. Product pages are generated statically for performance, then regenerated automatically when inventory or prices change.

**Content-heavy applications** — Blogs, documentation sites, news platforms all benefit from SSG/ISR. Fast loads, great SEO, easy content updates.

**Enterprise applications** — The conventions, structure, and tooling Next.js provides make it easier to onboard engineers, maintain consistency, and scale teams.

* * *

## Part 11: When React Alone May Be Enough

Next.js adds value — but it also adds complexity. There are situations where plain React is the better choice:

**Internal tools** — An admin dashboard used by 10 employees doesn't need SEO. It doesn't need SSR. A Create React App or Vite-powered SPA works perfectly and deploys anywhere without a Node.js server.

**Learning projects** — If you're learning React, start with React itself. Understanding components, state, props, and hooks before adding Next.js's conventions gives you a much stronger foundation.

**Simple SPAs with no public-facing pages** — A web app that lives entirely behind authentication (all users must log in before seeing anything) gets little benefit from SSR or SSG.

**Teams with specific tooling constraints** — Next.js requires a Node.js server or edge runtime for SSR/ISR. If your deployment environment only supports static files or has specific requirements, that might be a constraint.

The honest answer: if you're building something user-facing that will grow, Next.js is almost certainly the right choice from day one. If you're experimenting or building something internal, vanilla React is fine.

* * *

## Part 12: The Future of React Development

Here's a telling data point: the official React documentation now recommends starting new projects with a React framework — and Next.js is the first one listed. The React team and the Next.js team work together closely. Many of Next.js's features (Server Components, Streaming, Suspense-based data fetching) are actually React features that Next.js was the first to implement in production.

The trajectory is clear: modern React development is server-side-capable, full-stack by default, and structured around conventions rather than configuration. Next.js has driven that trajectory.

For most teams building user-facing products with React, the question isn't "should we use Next.js?" It's "is there a reason not to?"

* * *

## Summary

React is remarkable at what it does — building component-based user interfaces. But real production applications need more: server rendering, structured routing, optimized assets, SEO, data fetching patterns, and layouts.

Next.js provides all of that while staying completely true to React. It doesn't replace React — it completes it.

That's not a marketing pitch. It's the reason Netflix, Vercel, Twitch, Loom, Notion, and thousands of other companies made it their default.

* * *

*Happy building! 🚀*
