⚡ TECH BLOG
Home
Blog
Tags
About
⚡

Powered by Next.js 15 & Modern Web Tech ⚡

Back to Home

Next.js 13 App Router: A Complete Guide

January 15, 2023
nextjsreactapp-routerjavascript
Next.js 13 App Router: A Complete Guide

Next.js 13 App Router: A Complete Guide

The App Router is Next.js 13's revolutionary new routing system built on React Server Components.

App Directory Structure

app/
├── layout.tsx        # Root layout
├── page.tsx          # Home page
├── blog/
│   ├── layout.tsx    # Blog layout
│   ├── page.tsx      # Blog listing
│   └── [slug]/
│       └── page.tsx  # Individual blog post
├── loading.tsx       # Loading UI
├── error.tsx         # Error UI
└── not-found.tsx     # 404 page

Layouts

// app/layout.tsx
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <nav>Navigation</nav>
        {children}
        <footer>Footer</footer>
      </body>
    </html>
  );
}

Pages

// app/page.tsx
async function getPosts() {
  const res = await fetch('https://api.example.com/posts');
  return res.json();
}

export default async function Home() {
  const posts = await getPosts();
  
  return (
    <main>
      <h1>Blog Posts</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  );
}

Dynamic Routes

// app/blog/[slug]/page.tsx
async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`);
  return res.json();
}

export default async function PostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = await getPost(params.slug);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

// Generate static pages
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map(post => ({ slug: post.slug }));
}

Loading States

// app/loading.tsx
export default function Loading() {
  return (
    <div className="animate-pulse">
      <div className="h-8 bg-gray-200 rounded w-1/2 mb-4"></div>
      <div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
      <div className="h-4 bg-gray-200 rounded w-1/2"></div>
    </div>
  );
}

Error Handling

// app/error.tsx
'use client';

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

Server Actions

// app/actions.ts
'use server';

import { revalidatePath } from 'next/cache';

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  
  await fetch('https://api.example.com/posts', {
    method: 'POST',
    body: JSON.stringify({ title }),
  });
  
  revalidatePath('/blog');
}

// app/blog/new/page.tsx
import { createPost } from '../actions';

export default function NewPost() {
  return (
    <form action={createPost}>
      <input name="title" type="text" required />
      <button type="submit">Create</button>
    </form>
  );
}

Route Groups

app/
├── (marketing)/
│   ├── about/
│   │   └── page.tsx
│   └── layout.tsx
├── (shop)/
│   ├── products/
│   │   └── page.tsx
│   └── layout.tsx
└── layout.tsx

Metadata

// Static metadata
export const metadata = {
  title: 'My Blog',
  description: 'A blog about web development',
};

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

Conclusion

The App Router brings powerful new capabilities to Next.js. Server Components, nested layouts, and streaming make building web applications more intuitive and performant.

Share:

💬 Comments