React vs Next.js — What Actually Changes? A Beginner-Friendly Deep Dive

React vs Next.js — What Actually Changes? A Beginner-Friendly Deep Dive

React is one of the most popular libraries for building modern web apps. If you’ve learned React, you’re already on the right track. But as your app grows, you may notice problems:

  • Slow initial load times
  • Poor SEO performance
  • Excessive boilerplate for data fetching
  • Growing JavaScript bundle sizes

Next.js 15 addresses these issues by combining the best of React with powerful features like server-side rendering, built-in routing, and more. Let’s explore how React and Next.js 15 differ, step by step.

What Happens When You Visit a React App?

When a user visits a React app (e.g., built with create-react-app or Vite), here’s what happens:

  1. Browser Sends a Request
    The browser requests the URL (e.g., GET https://your-react-app.com/). The server responds with a minimal HTML file:

    <html> <head> <title>Your App</title> </head> <body> <div id="root"></div> <script src="/main.js"></script> </body> </html>

    This HTML contains an empty <div id="root"> and a script tag for the JavaScript bundle.

  2. JavaScript Bundle Execution

    • The browser downloads the entire React app’s JavaScript code.
    • React “hydrates” the empty <div id="root">, rendering the app in the browser.
    • Data fetching (e.g., via useEffect or axios) happens after the initial render, causing re-renders once data is available.

Example: E-commerce Landing Page in React

Consider a landing page with a header, navigation, and a list of featured products fetched from an API:

import { useState, useEffect } from 'react'; import axios from 'axios'; function LandingPage() { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { axios.get('/api/featured-products') .then((res) => { setProducts(res.data); setLoading(false); }); }, []); return ( <div> <header>Shop Now!</header> <nav>Home | Products | Cart</nav> <section> <h1>Welcome to Our Store</h1> {loading ? ( <p>Loading...</p> ) : ( <div> <h2>Featured Products</h2> <ul> {products.map(product => ( <li key={product.id}>{product.name}</li> ))} </ul> </div> )} </section> </div> ); }

Outcome:

  • The user sees a blank page or loading spinner until the JavaScript bundle downloads and the API call completes.
  • Search engines see only <div id="root">, harming SEO.
  • The entire app, including static components, is bundled into JavaScript, increasing load times.

Problems with Plain React (Client-Side Rendering)

  1. Poor SEO
    Search engine crawlers see an empty <div id="root"> with no meaningful content, leading to poor indexing and rankings.

  2. Slow First Load
    Users wait for the JavaScript bundle to download and execute before seeing content, resulting in a blank screen or spinner.

  3. Delayed Data Fetching
    Data is fetched client-side after the component mounts, causing additional renders and delays.

  4. Large JavaScript Bundles
    All components, even static ones, are included in the JavaScript bundle, increasing its size and slowing down the app.

How Next.js 15 Solves These Problems

Next.js 15 enhances React with features like server-side rendering (SSR), React Server Components, and the App Router. Here’s how it works:

Example: E-commerce Landing Page in Next.js 15

Using Next.js 15’s App Router and React Server Components, the same landing page is handled differently:

// app/page.tsx import { db } from "@/lib/db"; export default async function LandingPage() { const products = await db.getFeaturedProducts(); // Fetches on the server return ( <div> <header>Shop Now!</header> <nav>Home | Products | Cart</nav> <section> <h1>Welcome to Our Store</h1> <div> <h2>Featured Products</h2> <ul> {products.map(product => ( <li key={product.id}>{product.name} - ${product.price}</li> ))} </ul> </div> </section> </div> ); }

What Happens:

  1. The Next.js server fetches the data (e.g., featured products) and renders the component into HTML.
  2. The browser receives a fully rendered HTML page, including product data, which displays instantly.
  3. Search engines can index the content, improving SEO.

Adding Interactivity: For client-side interactivity (e.g., an “Add to Cart” button), use a client component:

// app/components/AddToCartButton.tsx 'use client'; import { useState } from 'react'; export function AddToCartButton({ productId }) { const [added, setAdded] = useState(false); return ( <button onClick={() => setAdded(!added)}> {added ? 'Added!' : 'Add to Cart'} </button> ); }

Integrate it into the server component:

// app/page.tsx (updated) import { db } from "@/lib/db"; import { AddToCartButton } from "@/components/AddToCartButton"; export default async function LandingPage() { const products = await db.getFeaturedProducts(); return ( <div> <header>Shop Now!</header> <nav>Home | Products | Cart</nav> <section> <h1>Welcome to Our Store</h1> <div> <h2>Featured Products</h2> <ul> {products.map(product => ( <li key={product.id}> {product.name} - ${product.price} <AddToCartButton productId={product.id} /> </li> ))} </ul> </div> </section> </div> ); }

Outcome:

  • The page loads instantly with pre-rendered content.
  • Only interactive components (e.g., AddToCartButton) are included in the JavaScript bundle, reducing its size.
  • SEO is improved because the HTML contains all content.

Extra Features in Next.js 15

  1. Built-in API Routes
    Create backend endpoints without a separate server:

    // app/api/contact/route.ts export async function POST(req) { const data = await req.json(); await db.save(data); return Response.json({ status: 'ok' }); }
  2. Server Actions
    Handle form submissions without API routes:

    // app/actions.ts 'use server'; export async function handleSubmit(data) { await db.save(data); }
  3. App Router & File-Based Navigation
    Define routes using the file system:

    app/
    ├─ layout.tsx        # Shared layout
    ├─ page.tsx          # /
    ├─ about/
    │  └─ page.tsx       # /about
    ├─ posts/
    │  └─ page.tsx       # /posts
    ├─ api/
    │  └─ hello/
    │     └─ route.ts    # /api/hello
    

Summary: React vs Next.js 15

FeatureReactNext.js 15
Initial HTMLBlank <div>Fully rendered content
SEOPoorExcellent
First LoadSlowFast
Data FetchingClient-side onlyServer-side by default
RoutingManual (React Router)File-based
Bundle SizeLargeSmaller (server components)
API SupportNeeds backendBuilt-in
Server LogicNot availableServer Actions supported

Final Thoughts

React is a powerful frontend library, but it requires manual handling of routing, SEO, and performance. Next.js 15 builds on React, offering:

  • Better performance with server-side rendering
  • SEO out of the box
  • Simplified data fetching
  • Built-in backend capabilities
  • Smaller JavaScript bundles
  • Clean architecture with the App Router

If you know React, Next.js 15 is the logical next step to build fast, scalable, and SEO-friendly web apps.

Published: 6/5/2025
Start Call