Base frameworks like React, Vue, and Svelte solve UI rendering. Meta-frameworks like Next.js, Nuxt, and SvelteKit sit on top of them to solve architecture: routing, server-side rendering (SSR), data fetching, API routes, and deployment. They are the modern standard for full-stack web applications.


Step 1 — Rendering Strategies

Understanding the Acronyms

  • CSR (Client-Side Rendering): Browser downloads empty HTML, loads JS, and renders the app. Bad for SEO, slow initial load.
  • SSR (Server-Side Rendering): Server runs the framework code per-request, sending fully populated HTML to the browser. Great for SEO, slower server response.
  • SSG (Static Site Generation): HTML is generated exactly once at build time. Extremely fast (served from CDN), but requires rebuild to update data.
  • ISR (Incremental Static Regeneration): Pages are built statically, but re-generated in the background when traffic hits after a specific timeout.

Step 2 — Next.js App Router Overview

The Next.js App Router utilizes React Server Components by default. Routing is defined by the file system using specific conventions.

Folder Structuretext
app/
├── layout.tsx       # Root layout (HTML skeleton, Navbar, Footer)
├── page.tsx         # The UI for route '/'
├── error.tsx        # Catches runtime errors
├── loading.tsx      # Suspense fallback shown while data fetches
└── dashboard/
    ├── layout.tsx   # Nested layout for dashboard routes
    ├── page.tsx     # Route: '/dashboard'
    └── [id]/        # Dynamic route parameter
        └── page.tsx # Route: '/dashboard/123'

Step 3 — Next.js Data Fetching & Caching

Next.js extends the native fetch API with powerful caching and revalidation options.

app/blog/page.tsxtsx
// Server Component (default in app router)
export default async function Blog() {
  // 1. Force cache (SSG equivalent)
  const ssgRes = await fetch('https://api.example.com/posts', { cache: 'force-cache' });
  
  // 2. No cache (SSR equivalent) - fetches on every request
  const ssrRes = await fetch('https://api.example.com/live', { cache: 'no-store' });
  
  // 3. Revalidate (ISR equivalent) - re-fetches background every 60s
  const isrRes = await fetch('https://api.example.com/stats', { 
    next: { revalidate: 60 }
  });

  const posts = await isrRes.json();

  return (
    <main>
      <h1>Latest Posts</h1>
      <ul>
        {posts.map((p: any) => <li key={p.id}>{p.title}</li>)}
      </ul>
    </main>
  );
}

Step 4 — Next.js Server Actions

Server Actions allow you to run server-side code directly from a client interaction (like a form submission) without writing a separate API route.

app/actions.tstsx
'use server'
import { revalidatePath } from 'next/cache';
import db from '@/lib/db';

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  
  // Database mutation runs strictly on the server
  await db.posts.insert({ title });
  
  // Purge cache so new data shows immediately
  revalidatePath('/blog');
}

// Usage in a component:
// <form action={createPost}>
//   <input name="title" />
//   <button type="submit">Post</button>
// </form>

Step 5 — Nuxt.js (Vue's Meta-Framework)

Nuxt provides a stellar developer experience with zero-config auto-imports and an incredible unified server engine called Nitro.

pages/index.vuevue
<script setup lang="ts">
// useFetch automatically runs on server during SSR, then passes payload to client
// No double-fetching! Auto-imported globally.
const { data: posts, pending, error } = await useFetch('/api/posts')
</script>

<template>
  <div>
    <h1>Nuxt Blog</h1>
    <div v-if="pending">Loading...</div>
    <div v-else-if="error">Error loading posts</div>
    <ul v-else>
      <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
    </ul>
  </div>
</template>
server/api/posts.tstypescript
// Nuxt Nitro Server Route
// Accessible at /api/posts
export default defineEventHandler(async (event) => {
  // Database logic here
  return [
    { id: 1, title: 'Nuxt is awesome' },
    { id: 2, title: 'Nitro makes it fast' }
  ]
})

Step 6 — SvelteKit Overview

SvelteKit pairs +page.svelte files (the UI) with +page.server.ts files (the data loader). It enforces a very clean separation between server logic and client rendering.

src/routes/profile/+page.server.tstypescript
import type { PageServerLoad } from './$types';

// Runs exclusively on the server
export const load: PageServerLoad = async ({ fetch, cookies }) => {
  const session = cookies.get('session');
  const res = await fetch(`/api/user/${session}`);
  const user = await res.json();

  // Return data to the page
  return { user };
};
src/routes/profile/+page.sveltehtml
<script lang="ts">
  import type { PageData } from './$types';
  
  // The data returned from the server load function is injected here
  export let data: PageData;
</script>

<h1>Welcome, {data.user.name}</h1>
<p>Email: {data.user.email}</p>