This episode covers SSR with Next.js: the concept of rendering on the server, hybrid rendering and static generation, modern data fetching patterns in the App Router, and guidance for choosing SSR, SSG, or SPA based on your app's needs.

An SPA renders everything in the browser; that's great for interactivity, but far from ideal for SEO and first paint on slow devices. Server-side rendering moves part of the rendering to the server. Episode 21 covers this through Next.js, the most popular React framework.
We learn the SSR concept, hybrid rendering with static generation, data fetching patterns in the Next.js App Router, and when to choose SSR, SSG, or SPA for your project.
Next.js is a React framework with file-system-based routing and per-page selectable rendering. Start a new project:
npx create-next-app@latest aplikasi-kunpx create-next-app@latest aplikasi-ku creates a complete project folder with TypeScript, ESLint, and the App Router structure. Run npm run dev to start, or npm run build for production.
Components in the App Router are by default Server Components: rendered on the server into HTML, then sent to the browser. JavaScript is only sent for the parts that are actually interactive. For interactive components, mark them with a directive:
"use client"
import { useState } from "react"
function TombolSuka() {
const [suka, setSuka] = useState(false)
return (
<button onClick={() => setSuka(!suka)}>
Disukai: {String(suka)}
</button>
)
}"use client" marks that this component and its descendants render on the client so they can use state and events. Other components stay server-rendered, producing HTML faster for the first-time user.
Next.js can combine many rendering strategies in one app. Static generation renders a page once at build time and serves it from the CDN. For data that needs to stay fresh, set revalidate:
export const revalidate = 60
export default async function HalamanBerita() {
const data = await fetch("https://api.example.com/berita").then((r) => r.json())
return <main>{data.map((item) => <p key={item.id}>{item.judul}</p>)}</main>
}export const revalidate = 60 makes Next.js keep the page in cache and rebuild it in the background every 60 seconds. This pattern is called ISR (incremental static regeneration) — static speed with data that stays fresh.
In the App Router, fetching is done directly inside the component with fetch, and Next.js adds automatic caching. The fetch call in HalamanBerita above is cached by default. For data that must always be current, bypass the cache:
export default async function HalamanStatus() {
const res = await fetch("https://api.example.com/status", { cache: "no-store" })
const data = await res.json()
return <p>Status: {data.status}</p>
}{ cache: "no-store" } inside the fetch call tells Next.js to always fetch the latest data — suitable for real-time data like service status. Choose the default caching for content that rarely changes, no-store for data that's alive.
For dynamic pages like a blog, create an app/blog/[slug] folder and export generateStaticParams so the page list is built at build time. Combine it with revalidate to get ISR on each route page.
Choose a strategy based on the page's characteristics:
Don't choose SSR just because it's trendy. Start with SSG for public pages, add ISR when data needs to be fresh, and use SSR only when the data genuinely depends on the request. Measure needs, not fashion. This decision often determines server cost and app speed.
Episode 21 introduced rendering React apps on the server via Next.js: Server Components and the client directive, hybrid rendering with SSG and ISR, modern data fetching patterns, and guidance for choosing a strategy per page.
Key takeaways:
"use client" directive limits interactivity to the client side.no-store for live data.In the next episode, episode 22, we'll cover observability & monitoring — frontend error logging and performance metrics, web vitals with synthetic and real user monitoring, crash reporting with Sentry, and user experience analytics and performance budgets. Your app will stay clearly visible even after launch.