Next.js 14 App Router: A Complete Guide

DK

David Kim

December 28, 2023
12 min read
Next.js 14 App Router: A Complete Guide
Next.jsReactApp RouterPerformance

Next.js 14 App Router: A Complete Guide

Next.js 14 introduces significant improvements to the App Router, making it more powerful and developer-friendly. This guide covers everything from basic concepts to advanced patterns.

What's New in Next.js 14

Performance Improvements

  • Turbopack: Faster local development with Rust-based bundler
  • Server Actions: Simplified server-side mutations
  • Partial Prerendering: Combine static and dynamic content seamlessly

Developer Experience

  • Improved Error Handling: Better error messages and debugging
  • Enhanced TypeScript Support: Stricter type checking
  • Metadata API: Simplified SEO and social sharing setup

App Router Fundamentals

File-Based Routing

The App Router uses a file-system based router where folders define routes:

text
app/
  page.tsx          // /
  about/
    page.tsx        // /about
  blog/
    page.tsx        // /blog
    [slug]/
      page.tsx      // /blog/[slug]

Special Files

  • page.tsx: UI for a route segment
  • layout.tsx: Shared UI for multiple pages
  • loading.tsx: Loading UI
  • error.tsx: Error UI
  • not-found.tsx: 404 UI

Layouts and Templates

Root Layout

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

Nested Layouts

text
// app/blog/layout.tsx
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="blog-layout">
      <aside>Blog Sidebar</aside>
      <main>{children}</main>
    </div>
  );
}

Server and Client Components

Server Components (Default)

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

export default async function PostsPage() {
  const posts = await getPosts();
  
  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}

Client Components

text
'use client';

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

Data Fetching Patterns

Parallel Data Fetching

text
async function getUser(id: string) {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

async function getPosts(userId: string) {
  const res = await fetch(`/api/posts?userId=${userId}`);
  return res.json();
}

export default async function UserPage({ params }: { params: { id: string } }) {
  // These requests run in parallel
  const userPromise = getUser(params.id);
  const postsPromise = getPosts(params.id);
  
  const [user, posts] = await Promise.all([userPromise, postsPromise]);
  
  return (
    <div>
      <h1>{user.name}</h1>
      <PostsList posts={posts} />
    </div>
  );
}

Streaming with Suspense

text
import { Suspense } from 'react';

export default function Page() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<div>Loading analytics...</div>}>
        <Analytics />
      </Suspense>
      <Suspense fallback={<div>Loading posts...</div>}>
        <RecentPosts />
      </Suspense>
    </div>
  );
}

Server Actions

Form Handling

text
// app/contact/page.tsx
async function submitForm(formData: FormData) {
  'use server';
  
  const name = formData.get('name') as string;
  const email = formData.get('email') as string;
  
  // Process form data
  await saveContact({ name, email });
  
  redirect('/thank-you');
}

export default function ContactPage() {
  return (
    <form action={submitForm}>
      <input name="name" placeholder="Name" required />
      <input name="email" type="email" placeholder="Email" required />
      <button type="submit">Submit</button>
    </form>
  );
}

Advanced Patterns

Route Groups

text
app/
  (marketing)/
    about/
      page.tsx      // /about
    contact/
      page.tsx      // /contact
  (shop)/
    products/
      page.tsx      // /products

Parallel Routes

text
app/
  dashboard/
    @analytics/
      page.tsx
    @team/
      page.tsx
    layout.tsx
    page.tsx

Intercepting Routes

text
app/
  feed/
    page.tsx
    @modal/
      (..)photo/
        [id]/
          page.tsx    // Intercepts /photo/[id]

Migration from Pages Router

Key Differences

  1. File Structure: pages/app/
  2. API Routes: pages/api/app/api/
  3. Data Fetching: getServerSideProps → Server Components
  4. Layouts: HOCs → layout.tsx files

Migration Strategy

  1. Start with new routes in app/
  2. Gradually migrate existing pages
  3. Update data fetching patterns
  4. Refactor layouts and components

Best Practices

  1. Use Server Components by Default: Only use Client Components when needed
  2. Colocate Related Files: Keep components, styles, and tests together
  3. Optimize Loading States: Use Suspense boundaries strategically
  4. Handle Errors Gracefully: Implement proper error boundaries
  5. Leverage Caching: Understand Next.js caching behavior

Conclusion

Next.js 14's App Router represents a significant evolution in React development. By embracing Server Components, improved data fetching, and modern patterns, you can build faster, more maintainable applications. The learning curve is worth the investment for the performance and developer experience benefits.

The App Router isn't just a new routing system—it's a new way of thinking about React applications.

Read Next

Docker for Frontend Developers
30 Dec 2023
6 min read

Learn how Docker can streamline your frontend development workflow and deployment process.

WebAssembly: The Future of High-Performance Web Apps
21 Dec 2023
10 min read

Explore WebAssembly (WASM) and how it enables near-native performance for web applications.

Building Scalable React Applications with TypeScript
15 Jan 2024
8 min read

Learn how to structure large React applications using TypeScript for better maintainability and developer experience.