---
title: "02 — Next.js Foundations — JavaScript, React, and the SPA-vs-SSR Question"
uid: nextjs-foundations
tags: ["roadmap:nextjs", "react", "ssr", "spa", "javascript", "nextjs"]
excerpt: "Next.js is React plus opinions, and React is JavaScript — the prerequisites cascade. Its first real decision: where the HTML gets generated, on the server or in the browser."
date: 2026-08-13T03:28:03+0000
source: https://www.aveshina.my.id/en/blog/nextjs-foundations
---

Every Next.js concept felt arbitrary until I walked the dependency chain underneath it. The model that clicked: **Next.js is React plus opinions, and React is JavaScript** — so the dependency chain runs straight through language fundamentals into component model into rendering strategy. Skip the chain and every Next.js concept feels arbitrary; walk it in order and the framework's choices start to look inevitable [1][4].

The other thing I had to nail down before anything else was the **SPA vs. SSR** distinction, because Next.js's whole reason for existing is letting you pick between them per route instead of once, globally [5].

## JavaScript first — the non-negotiable prerequisite

Next.js is built on top of React, and React is a JavaScript library [3][4]. Trying to use Next.js without solid JavaScript fundamentals is like trying to write poetry in a language you can't order coffee in — every framework concept collides with the language gap underneath it. The roadmap is blunt about this: without a solid grasp of JavaScript fundamentals, Next.js's features and concepts won't make sense [2].

The JavaScript I actually reach for daily in Next.js:

- ES modules (import/export) — the entire module system.
- Async/await and Promises — every data fetch is async.
- Destructuring and spread — component props are built from these.
- Array methods (map, filter, reduce) — rendering lists is map.
- Closures and scope — hooks live or die on understanding these.

The way of thinking that helped: HTML is the structure of a house, CSS is the interior design, JavaScript is the electrical system that makes everything work [2]. Next.js sits on top of all three, and the electricity is the part that bites if it's shaky.

## React — the component model underneath

React is a declarative, component-based library for building UIs [3]. The idea worth internalizing is that a React component is a function: data in (props, state), UI out. The library handles reconciling changes to that UI efficiently. Next.js does not replace this model — it embraces it, then adds Server Components on top so the function can run on the server instead of the browser.

The framework question is downstream of this. Building modern web apps involves managing complex UIs, handling data, and ensuring smooth UX — frameworks like React, Angular, and Next.js provide structure for these challenges so I focus on features instead of reinventing infrastructure [1]. React is the rendering core; Next.js is the structure around it.

## The SPA vs. SSR decision

This is the conceptual fork that everything else in Next.js branches from [5]:

- **Single-Page Application (SPA)** loads one HTML page, then JavaScript updates the DOM dynamically as the user interacts — no full page reloads. Fluid UX, but the browser does the rendering work, and the initial HTML is mostly empty until JS runs.
- **Server-Side Rendering (SSR)** generates the HTML on the server for each request and sends a fully rendered page to the client. The user sees content immediately; search engines see real HTML.

```figure
<svg viewBox="0 0 720 300" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="SPA versus SSR request flow. Top row (SPA): browser sends GET / to server, server returns a near-empty HTML shell plus a large JS bundle, browser runs JS and renders content late. Bottom row (SSR): browser sends GET / to server, server renders HTML itself and returns a fully-populated HTML page, browser paints content immediately.">
  <defs>
    <marker id="fa2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- SPA row -->
    <text x="360" y="22" font-size="12" font-weight="700" fill="#7f1d1d" text-anchor="middle">SPA — render in the browser</text>
    <rect x="30" y="38" width="110" height="44" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="85" y="64" font-size="11" font-weight="700" fill="#7f1d1d" text-anchor="middle">browser</text>
    <path d="M140,60 L218,60" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#fa2)"/>
    <text x="179" y="52" font-size="9" fill="#475569" text-anchor="middle">GET /</text>
    <rect x="220" y="38" width="120" height="44" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="280" y="58" font-size="10" font-weight="700" fill="#422006" text-anchor="middle">server</text>
    <text x="280" y="72" font-size="9" fill="#422006" text-anchor="middle">empty shell + JS</text>
    <path d="M340,60 L418,60" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#fa2)"/>
    <rect x="420" y="38" width="130" height="44" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="485" y="58" font-size="10" font-weight="700" fill="#7f1d1d" text-anchor="middle">JS runs</text>
    <text x="485" y="72" font-size="9" fill="#7f1d1d" text-anchor="middle">renders late</text>
    <text x="640" y="64" font-size="10" font-style="italic" fill="#7f1d1d" text-anchor="middle">content appears last</text>

    <!-- SSR row -->
    <text x="360" y="160" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">SSR — render on the server</text>
    <rect x="30" y="176" width="110" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="85" y="202" font-size="11" font-weight="700" fill="#052e16" text-anchor="middle">browser</text>
    <path d="M140,198 L218,198" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#fa2)"/>
    <text x="179" y="190" font-size="9" fill="#475569" text-anchor="middle">GET /</text>
    <rect x="220" y="176" width="120" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="280" y="196" font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">server</text>
    <text x="280" y="210" font-size="9" fill="#052e16" text-anchor="middle">renders HTML</text>
    <path d="M340,198 L418,198" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#fa2)"/>
    <rect x="420" y="176" width="130" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="485" y="196" font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">full HTML</text>
    <text x="485" y="210" font-size="9" fill="#052e16" text-anchor="middle">paints at once</text>
    <text x="640" y="202" font-size="10" font-style="italic" fill="#052e16" text-anchor="middle">content appears first</text>

    <text x="360" y="270" font-size="11" fill="#64748b" text-anchor="middle" font-style="italic">Next.js lets you choose per route — not once for the whole app</text>
  </g>
</svg>
```

The trade-offs are the whole story:

- **SPA** gives fluid navigation after the initial load, at the cost of a slow first paint and poor SEO (the crawler sees an empty shell).
- **SSR** gives fast first paint and good SEO, at the cost of server work per request.

The thing Next.js actually does is refuse to force this choice globally — a marketing page can be SSR for SEO, a dashboard can be SPA for interactivity, a blog can be statically generated at build time. The framework's core value proposition is the menu of rendering strategies and the per-route switch [5].

## Why this matters before going further

Every later concept in Next.js — App Router, Server Components, caching, data fetching — is an elaboration of one of these two threads. The Server Components model is SSR turned into the default rendering mode. The Pages-vs-App Router split is partly about how deeply SSR is integrated. Caching is the lever that makes SSR affordable. If the SPA-vs-SSR fork is fuzzy, none of those lands cleanly.

## How I use this

Before I reach for any Next.js feature, I check the prerequisites are actually in place: is the JavaScript solid? Is the React component model understood? And for each route I build, I ask the one question that drives everything else — *where should this HTML be generated?* Server, client, or build-time. The answer dictates the rest of the implementation.

## References

[1] "Which JS framework is best?," YouTube, 2023. [Video]. Available: [https://www.youtube.com/watch?v=cuHDQhDhvPE](https://www.youtube.com/watch?v=cuHDQhDhvPE)

[2] "The modern JavaScript tutorial," javascript.info, 2024. [Online]. Available: [https://javascript.info/](https://javascript.info/)

[3] Meta Open Source, "React — The library for web and native user interfaces," react.dev, 2024. [Online]. Available: [https://react.dev](https://react.dev)

[4] Vercel, "Next.js — The React framework for the web," nextjs.org, 2024. [Online]. Available: [https://nextjs.org/](https://nextjs.org/)

[5] "SSR vs. SPA showdown: choosing the right rendering approach for your web app," dev.to, 2024. [Online]. Available: [https://dev.to/santhanam87/ssr-vs-spa-showdown-choosing-the-right-rendering-approach-for-your-web-app-4439](https://dev.to/santhanam87/ssr-vs-spa-showdown-choosing-the-right-rendering-approach-for-your-web-app-4439)

[6] Vercel, "How to build single-page applications with Next.js," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/guides/single-page-applications](https://nextjs.org/docs/app/guides/single-page-applications)

```quiz
Q: What's the prerequisite chain for Next.js, in order?
- React → JavaScript → Next.js
- JavaScript → React → Next.js
correct: 1
explain: Next.js is built on React, which is a JavaScript library. Solid JavaScript fundamentals come first, then React's component model, then Next.js's opinions on top.

Q: In an SPA, when does the user see the actual page content?
- Immediately — the server sends fully rendered HTML
- After the JavaScript bundle downloads, parses, and runs
correct: 1
explain: An SPA loads a near-empty HTML shell, then JavaScript renders the content. The initial paint is delayed until the JS executes.

Q: What is the core advantage SSR has over SPA for a marketing site?
- Smaller JavaScript bundle
- Faster first paint of real content and better SEO, because the server sends fully rendered HTML
correct: 1
explain: SSR generates HTML on the server per request, so the browser (and crawlers) receive fully populated HTML immediately. That helps both perceived load time and search indexing.

Q: Next.js forces you to pick SPA or SSR for the entire application. True or false?
- True — it's a global setting
- False — you can choose per route
correct: 1
explain: One of Next.js's core value propositions is letting you mix rendering strategies — SPA, SSR, SSG, ISR — on a per-route basis instead of committing globally.
```
