This episode covers SSR and Nuxt: the basics of server-side rendering, hybrid rendering and static generation, Nuxt's data fetching model along with middleware, plus guidance on choosing SSR over an SPA according to application needs.

The SPA built in previous episodes renders pages in the browser. The consequence: the browser has to download the bundle first, so the first page doesn't contain content immediately. SSR answers this by rendering the HTML on the server.
Episode 21 covers server-side rendering with Nuxt.js: SSR basics, hybrid rendering and static generation, Nuxt's characteristic data fetching model with middleware, and guidance on when to choose SSR over an SPA.
In SSR, the server runs Vue, renders the page into HTML, and sends the finished HTML to the browser. Content shows immediately, and JavaScript hydrates for interactivity. The result: a faster first page and better SEO.
npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm installnpx nuxi@latest init creates a Nuxt project with the pages/, components/, and app.vue structure. Run it with:
npm run devnpm run dev starts the server with SSR and hot reload. Nuxt uses the folder structure for automatic routing, unlike manual vue-router.
Nuxt can mix rendering per route through routeRules:
export default defineNuxtConfig({
routeRules: {
"/": { prerender: true },
"/blog/**": { swr: true },
"/api/**": { isr: true },
},
});prerender: true makes the home page static HTML at build time, while swr and isr serve cached versions refreshed behind the scenes. A single application can use several strategies at once.
nuxt generate produces the entire site as static files:
npm run generatenpm run generate renders every page at build time and writes the results into the dist folder. The output can be hosted on any CDN, including the platforms from episode 20.
Nuxt provides useFetch, which combines fetching and state:
<script setup lang="ts">
const { data: produk, pending } = await useFetch("/api/produk");
</script>
<template>
<div v-if="pending">Memuat...</div>
<ul>
<li v-for="item in produk" :key="item.id">{{ item.nama }}</li>
</ul>
</template>useFetch("/api/produk") runs on the server during SSR, sending the data together with the HTML. The browser doesn't need an extra request for the first page.
Nuxt middleware runs before a page is rendered:
export default defineNuxtRouteMiddleware((to) => {
if (!isLogin()) {
return navigateTo("/login");
}
});defineNuxtRouteMiddleware((to) => ...) checks authentication before rendering. A file named auth.ts in the middleware folder is applied by adding a middleware property on the page.
Tip
SSR isn't magic: server costs rise and TTFB can be slower for complex pages. Measure the trade-off, and start from static generation before moving up to full SSR.
Episode 21 opened the world of SSR and Nuxt: the reasoning behind server-side rendering, hybrid rendering with routeRules and static generation, data fetching with useFetch and middleware, and guidance for choosing between SSR, SPA, and static sites.
Key takeaways:
routeRules mixes prerender, SWR, and ISR.nuxt generate produces a static site.useFetch runs on the server for initial data.In the next episode 22, we'll cover observability and monitoring — monitoring frontend performance, tracking errors with Sentry, real user monitoring and analytics, plus production support and incident handling.