Getting Started with Next.js 13: App Router Deep Dive

Explore the powerful new App Router in Next.js 13, featuring server components, nested layouts, and improved developer experience for modern React applications.

Nosgnoh
January 15, 2024
4 minute read
Getting Started with Next.js 13: App Router Deep Dive

Getting Started with Next.js 13: App Router Deep Dive

Next.js 13 introduced a revolutionary new paradigm with the App Router, fundamentally changing how we build React applications. This comprehensive guide will walk you through everything you need to know about the App Router, from basic concepts to advanced patterns.

What is the App Router?

The App Router is a new routing system built on top of React Server Components. It allows you to:

  • Co-locate data fetching with your components
  • Stream content as it becomes available
  • Nest layouts for better code organization
  • Handle loading and error states declaratively

Key Features

1. File-based Routing

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

app/
├── page.tsx          // Route: /
├── about/
│   └── page.tsx      // Route: /about
└── blog/
    ├── page.tsx      // Route: /blog
    └── [slug]/
        └── page.tsx  // Route: /blog/[slug]

2. Server Components by Default

Components in the app directory are Server Components by default:

// app/blog/page.tsx
async function BlogPage() {
  // This runs on the server
  const posts = await fetch('https://api.example.com/posts')
    .then(res => res.json());

  return (
    <div>
      <h1>Blog Posts</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}

export default BlogPage;

3. Nested Layouts

Create shared layouts that wrap multiple pages:

// app/blog/layout.tsx
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="blog-layout">
      <nav>
        <Link href="/blog">All Posts</Link>
        <Link href="/blog/categories">Categories</Link>
      </nav>
      <main>{children}</main>
    </div>
  );
}

Advanced Patterns

Data Fetching Strategies

The App Router provides several patterns for data fetching:

// Static data (built at build time)
async function getStaticPosts() {
  const posts = await fetch('https://api.example.com/posts', {
    cache: 'force-cache' // Default behavior
  });
  return posts.json();
}

// Dynamic data (fetched on each request)
async function getDynamicPosts() {
  const posts = await fetch('https://api.example.com/posts', {
    cache: 'no-store'
  });
  return posts.json();
}

// Revalidated data (cached with revalidation)
async function getRevalidatedPosts() {
  const posts = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 } // Revalidate every 60 seconds
  });
  return posts.json();
}

Loading and Error Handling

Handle loading and error states with special files:

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

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

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div className="text-center py-12">
      <h2>Something went wrong!</h2>
      <p className="text-gray-600 mt-2">{error.message}</p>
      <button
        onClick={reset}
        className="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
      >
        Try again
      </button>
    </div>
  );
}

Best Practices

1. Use Server Components When Possible

Server Components provide better performance and SEO:

// ✅ Good: Server Component for static content
async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <PostContent content={post.content} />
    </article>
  );
}

// ✅ Good: Client Component for interactivity
'use client';
function InteractiveComments({ postId }: { postId: string }) {
  const [comments, setComments] = useState([]);
  
  return (
    <div>
      {/* Interactive comment system */}
    </div>
  );
}

2. Optimize Data Fetching

// ✅ Good: Parallel data fetching
async function BlogPage() {
  const [posts, categories] = await Promise.all([
    getPosts(),
    getCategories()
  ]);

  return (
    <div>
      <CategoryFilter categories={categories} />
      <PostList posts={posts} />
    </div>
  );
}

3. Handle Streaming Content

// app/blog/page.tsx
import { Suspense } from 'react';

export default function BlogPage() {
  return (
    <div>
      <h1>Latest Posts</h1>
      <Suspense fallback={<PostsSkeleton />}>
        <Posts />
      </Suspense>
      <Suspense fallback={<CategoriesSkeleton />}>
        <Categories />
      </Suspense>
    </div>
  );
}

Migration from Pages Router

If you're migrating from the Pages Router:

  1. Move files from pages/ to app/
  2. Rename _app.tsx to layout.tsx
  3. Update data fetching methods
  4. Convert to Server Components where possible

Conclusion

The App Router represents a significant evolution in React development, offering better performance, developer experience, and user experience. While there's a learning curve, the benefits of server components, improved data fetching, and better code organization make it worth the investment.

Start small by creating new routes with the App Router, then gradually migrate existing pages as you become more comfortable with the new patterns.

Resources


Ready to dive deeper into Next.js? Check out my next post on Advanced Server Components Patterns for more advanced techniques and patterns.

Tags

#next.js#react#typescript#app-router#server-components
All Posts
Share this post with your network