React

Next.js 15 App Router: Everything You Need to Know

dev.prakah2011 April 28, 2025 2 min read
⚛️

Next.js 15 ships with significant improvements to the App Router — the React Server Component-first routing system introduced in Next.js 13. If you’re still on the Pages Router or are hesitant about the App Router’s complexity, this guide will bring you up to speed on what’s changed and why it matters.

Server Components vs Client Components

The App Router treats every component as a React Server Component (RSC) by default. RSCs render on the server and ship zero JavaScript to the client. Only components explicitly marked with 'use client' are hydrated in the browser.

// app/page.tsx — Server Component (default)
async function BlogPage() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());
  return (
    <ul>
      {posts.map(p => <li key={p.id}>{p.title}</li>)}
    </ul>
  );
}

// components/LikeButton.tsx — Client Component
'use client';
import { useState } from 'react';

export function LikeButton() {
  const [liked, setLiked] = useState(false);
  return <button onClick={() => setLiked(!liked)}>{liked ? '❤️' : '🤍'}</button>;
}

Parallel Routes

Parallel routes let you render multiple pages simultaneously in the same layout using @slot conventions. This is ideal for dashboards where a sidebar and main content area load independently.

app/
  dashboard/
    layout.tsx
    @analytics/
      page.tsx     ← renders independently
    @team/
      page.tsx     ← renders independently
    page.tsx

Streaming with Suspense

Next.js 15 makes progressive streaming first-class. Wrap slow data-fetching components in <Suspense> and they stream in as they resolve — the shell renders immediately.

import { Suspense } from 'react';

export default function Page() {
  return (
    <>
      <h1>Dashboard</h1>
      <Suspense fallback={<Skeleton />}>
        <SlowDataComponent />
      </Suspense>
    </>
  );
}

The New Metadata API

Static and dynamic metadata is now handled through a typed metadata export or a generateMetadata async function — no more custom <Head> components.

// Static
export const metadata = {
  title: 'DevForge Blog',
  description: 'Expert web development tutorials.',
};

// Dynamic
export async function generateMetadata({ params }) {
  const post = await getPost(params.slug);
  return { title: post.title, description: post.excerpt };
}

Key Takeaways

  • Default to Server Components and only opt into 'use client' when you need interactivity or browser APIs
  • Use Parallel Routes for dashboard-style layouts with independent loading states
  • Wrap slow components in <Suspense> to ship the shell immediately
  • Replace <Head> with the Metadata API for better SEO control
dev.prakah2011
dev.prakah2011

Developer & author at DevForge Agency.

Related Articles

⚛️
React

Tailwind CSS + shadcn/ui: Building a Design System