Getting Started with Next.js App Router
Learn how to build modern web applications with Next.js 14's powerful App Router and React Server Components.
Getting Started with Next.js App Router
The Next.js App Router is a paradigm shift in how we build React applications. It introduces React Server Components, improved data fetching, and a more intuitive file-based routing system.
What is the App Router?
The App Router is Next.js’s new routing system introduced in version 13. It uses the app directory instead of the traditional pages directory, bringing several powerful features:
- Server Components by default - Better performance and smaller bundle sizes
- Streaming and Suspense - Progressive rendering for faster page loads
- Simplified data fetching - No more
getServerSidePropsorgetStaticProps - Layouts and Templates - Share UI across routes easily
Creating Your First Route
Creating routes in the App Router is straightforward. Each folder in the app directory represents a route segment:
// app/blog/page.tsx
export default function BlogPage() {
return (
<main>
<h1>My Blog</h1>
<p>Welcome to my blog!</p>
</main>
);
}
This creates a route at /blog. The page.tsx file is what makes the route publicly accessible.
Server Components vs Client Components
By default, all components in the App Router are Server Components. This means they run on the server and don’t ship JavaScript to the client.
// Server Component (default)
async function BlogPosts() {
const posts = await fetchPosts(); // Runs on server
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
</article>
))}
</div>
);
}
When you need interactivity, use the "use client" directive:
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
Data Fetching Made Simple
No more getServerSideProps! Just use async/await directly in your components:
async function BlogPost({ params }: { params: { slug: string } }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`)
.then(res => res.json());
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
Layouts for Shared UI
Layouts let you share UI across multiple pages:
// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<nav>
<a href="/blog">All Posts</a>
</nav>
<main>{children}</main>
<footer>© 2024 My Blog</footer>
</div>
);
}
This layout wraps all pages under /blog/*.
Loading and Error States
The App Router provides special files for handling loading and error states:
// app/blog/loading.tsx
export default function Loading() {
return <div>Loading blog posts...</div>;
}
// app/blog/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>
);
}
Metadata for SEO
Generate metadata for each page easily:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "My Blog",
description: "A blog about web development",
};
export default function BlogPage() {
return <div>...</div>;
}
Best Practices
- Keep Server Components as default - Only use
"use client"when needed - Fetch data close to where it’s used - No prop drilling required
- Use loading.tsx for better UX - Show loading states automatically
- Leverage route groups - Organize routes without affecting URLs
- Optimize images - Use Next.js Image component for automatic optimization
Conclusion
The App Router represents the future of Next.js development. While there’s a learning curve, the benefits of Server Components, simplified data fetching, and improved performance make it worth the transition.
Start small, experiment with the new patterns, and gradually migrate your existing applications. The Next.js team has done an excellent job making this transition as smooth as possible.
Happy coding! 🚀
Resources: