Skip to main content
DailyKiUpdate
Technology

Next.js App Router Tutorial: Server Components, Routing & Data Fetching

Learn Next.js App Router with practical examples covering Server Components, Client Components, nested layouts, dynamic routes, project structure, and data fetching.

By Rajendra KumarUpdated November 1, 20269 min read264 views
Next.js App Router Tutorial: Server Components, Routing & Data Fetching

Next.js App Router Tutorial: Server Components, Routing and Data Fetching

The Next.js App Router is the modern routing architecture for building React applications with Next.js. It introduces a new way of organizing routes, layouts, data fetching, and components using the app directory and React Server Components.

If you're learning Next.js or migrating from the Pages Router, understanding the App Router is one of the most important steps toward building modern Next.js applications.

In this Next.js App Router tutorial, we'll cover:

  • What the Next.js App Router is

  • App Router project structure

  • Server Components

  • Client Components

  • Nested layouts

  • Dynamic routes

  • Loading and error states

  • Data fetching

  • Caching and revalidation

  • App Router vs Pages Router

  • Practical Next.js App Router best practices


What Is the Next.js App Router?

The Next.js App Router is a file-system-based routing system built around the app directory.

A typical Next.js application using the App Router may look like this:

app/
├── layout.tsx
├── page.tsx
├── about/
│   └── page.tsx
├── blog/
│   ├── page.tsx
│   └── [slug]/
│       └── page.tsx
└── dashboard/
    ├── layout.tsx
    └── page.tsx

The folder structure directly maps to URL routes.

For example:

app/page.tsx

creates:

/

While:

app/about/page.tsx

creates:

/about

A dynamic route can be created with:

app/blog/[slug]/page.tsx

which can handle URLs such as:

/blog/nextjs-app-router
/blog/typescript-generics

This file-based approach makes routing easier to organize as an application grows.


Next.js App Router Project Structure

Understanding the special files in the app directory is important.

Common App Router files include:

File

Purpose

page.tsx

Defines a route's UI

layout.tsx

Shared UI that persists across routes

loading.tsx

Loading UI

error.tsx

Error UI

not-found.tsx

Not-found UI

route.ts

Creates an API endpoint

template.tsx

Similar to a layout but remounts on navigation

For example:

app/
├── layout.tsx
├── page.tsx
├── loading.tsx
├── not-found.tsx
└── blog/
    ├── page.tsx
    └── [slug]/
        └── page.tsx

This structure allows routing and UI behavior to live together.


Server Components in Next.js

One of the biggest changes introduced with the App Router is the use of React Server Components.

Components inside the App Router are Server Components by default.

For example:

// app/page.tsx

export default async function Page() {
  const response = await fetch(
    "https://api.example.com/posts"
  );

  const posts = await response.json();

  return <PostList posts={posts} />;
}

The component can be asynchronous and retrieve data before rendering.

This is useful when a component doesn't need browser-only functionality or interactive state.

Server Components can help reduce the amount of JavaScript that needs to be sent to the browser.


Client Components in Next.js

Not every component should run exclusively on the server.

If a component needs browser-side interactivity such as:

  • useState

  • useEffect

  • Event handlers

  • Browser APIs

  • Interactive UI

you can make it a Client Component.

Add "use client" at the top of the file:

"use client";

import { useState } from "react";

export function LikeButton() {
  const [liked, setLiked] = useState(false);

  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? "♥" : "♡"}
    </button>
  );
}

The "use client" directive tells Next.js that this component belongs to the client-side component boundary.


Server Components vs Client Components

A simple way to decide which component to use is:

Use a Server Component when the component primarily needs server-side data or rendering.

Use a Client Component when the component requires browser-side interactivity or state.

For example:

Page
│
├── Header          → Server Component
├── BlogList        → Server Component
│
└── LikeButton      → Client Component

This approach allows interactive components to remain client-side without turning the entire page into a Client Component.


Nested Layouts in Next.js

Nested layouts are another important feature of the App Router.

Suppose you have:

app/
└── dashboard/
    ├── layout.tsx
    ├── page.tsx
    ├── users/
    │   └── page.tsx
    └── settings/
        └── page.tsx

You can create a shared dashboard layout:

// app/dashboard/layout.tsx

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="flex">
      <Sidebar />

      <main>
        {children}
      </main>
    </div>
  );
}

The layout can provide a persistent sidebar while the page content changes.

For example:

/dashboard
/dashboard/users
/dashboard/settings

can all share the same dashboard layout.

This makes nested layouts particularly useful for:

  • Admin dashboards

  • Account pages

  • Documentation websites

  • E-commerce applications

  • SaaS applications


Dynamic Routes With App Router

Dynamic routes allow you to create pages based on URL parameters.

For example:

app/blog/[slug]/page.tsx

The [slug] segment is dynamic.

A page can access the parameter:

export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;

  return <h1>{slug}</h1>;
}

A URL such as:

/blog/nextjs-app-router

would provide:

slug = "nextjs-app-router"

Dynamic routes are especially useful for blogs, products, documentation, and news websites.


Data Fetching in Next.js App Router

Data fetching is one of the areas where the App Router changes the traditional Next.js development model.

With the Pages Router, developers commonly used:

getServerSideProps
getStaticProps
getStaticPaths

The App Router allows data fetching directly inside Server Components.

For example:

export default async function PostsPage() {
  const response = await fetch(
    "https://api.example.com/posts"
  );

  const posts = await response.json();

  return <PostList posts={posts} />;
}

This makes the component responsible for requesting the data it needs.


Caching and Revalidation

Next.js provides different approaches for controlling how fetched data is cached and refreshed.

For example:

fetch(url, {
  cache: "force-cache",
});

can be used when cached data is appropriate.

For periodically refreshed data:

fetch(url, {
  next: {
    revalidate: 60,
  },
});

This tells Next.js to revalidate the data according to the configured interval.

For data that should be requested without caching:

fetch(url, {
  cache: "no-store",
});

The important concept is to choose the caching strategy based on how frequently the underlying data changes.

For a blog article, cached or statically generated content may be appropriate.

For frequently changing information such as dashboards or live data, a different strategy may be required.


Loading UI With loading.tsx

The App Router also provides a convention for loading states.

You can create:

app/blog/loading.tsx

For example:

export default function Loading() {
  return <p>Loading articles...</p>;
}

Next.js can display this UI while the relevant route segment is loading.

This is useful for creating better perceived performance and providing immediate feedback to users.


Error Handling With error.tsx

You can also create an error boundary for a route segment:

app/blog/error.tsx

Example:

"use client";

export default function Error({
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong.</h2>

      <button onClick={() => reset()}>
        Try again
      </button>
    </div>
  );
}

This allows applications to handle rendering errors gracefully instead of showing an unhelpful blank page.


Route Handlers in Next.js App Router

The App Router also provides Route Handlers for creating backend API endpoints.

For example:

app/api/posts/route.ts

can define an API endpoint.

export async function GET() {
  return Response.json({
    success: true,
    message: "Posts fetched successfully",
  });
}

This creates an endpoint at:

/api/posts

Route Handlers can be useful when you need backend functionality directly inside a Next.js application.


App Router vs Pages Router

The two routing architectures can be compared conceptually:

Feature

App Router

Pages Router

Main directory

app/

pages/

Server Components

Yes

No by default

Nested layouts

Built-in

Requires additional patterns

getServerSideProps

Not used

Common

getStaticProps

Not used

Common

Route Handlers

Yes

API routes

Loading UI

loading.tsx

Custom implementation

Error UI

error.tsx

Custom implementation

Dynamic routes

[slug]

[slug]

The App Router is designed around React Server Components and colocated routing conventions, while the Pages Router follows the older Next.js architecture.


Next.js App Router Best Practices

When building a production application with the App Router, consider these practices.

1. Keep Server Components as the default

Don't add "use client" unless the component actually requires client-side functionality.

2. Keep Client Components small

Instead of making an entire page interactive, isolate the interactive part into a smaller Client Component.

3. Fetch data close to where it is needed

Server Components make it easier to colocate data requirements with UI.

4. Use layouts for shared UI

Headers, sidebars, navigation, and dashboard shells are good candidates for layouts.

5. Choose caching based on data freshness

Static content and frequently changing data don't necessarily need the same caching strategy.

6. Use loading and error states

loading.tsx and error.tsx can make applications feel more responsive and resilient.

7. Optimize metadata and SEO

The App Router supports Next.js metadata APIs, making it possible to define page titles, descriptions, canonical URLs, Open Graph information, and other metadata close to the route.


Why Learn the Next.js App Router?

The App Router is important for developers building modern Next.js applications because it brings routing, layouts, Server Components, data fetching, loading states, error handling, and backend route handlers into a unified architecture.

Instead of thinking:

"Which special data-fetching function should I use?"

the App Router encourages a different mental model:

Where should this code run, what data does this component need, and how fresh does that data need to be?

Once this mental model becomes familiar, the App Router becomes much easier to work with.


Final Takeaway

The Next.js App Router is more than a replacement for the pages/ directory. It introduces a different way of structuring React applications around Server Components, Client Components, nested layouts, dynamic routing, and integrated data fetching.

The most important concepts to understand are:

  • app/ directory

  • Server Components

  • Client Components

  • Nested layouts

  • Dynamic routes

  • Data fetching

  • Caching and revalidation

  • Loading states

  • Error handling

  • Route Handlers

  • Metadata and SEO

If you're starting a new Next.js project, learning these concepts will give you a strong foundation for building scalable, performant, and SEO-friendly web applications.

The key mental shift is simple:

Think about where your component runs, what data it needs, how interactive it needs to be, and how fresh that data should remain.

That approach makes the Next.js App Router much easier to understand and use effectively.

Share this article

Comments