aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Web Development

Next.js App Router: The app Directory and Nested Routes

The Next.js App Router is the routing system that became stable in Next.js 13 and replaces the older pages/ directory. By mapping your folder structure directly to URLs, it brings React Server Components, nested layouts and streaming as defaults. In this guide we'll build, step by step and with real examples, how the app/ directory works, what the special files do, and how nested routing is wired together.

Why Does the app Directory Exist?

In the old pages/ system, every file was a page and data fetching relied on special functions like getServerSideProps. The App Router is folder-based: a folder is a route segment, and the special files inside it define that segment's behavior. The biggest difference is that components are Server Components by default — they render on the server without shipping JavaScript to the browser.

The healthiest way to start a project with the App Router is the official scaffolding tool:

npx create-next-app@latest my-project
cd my-project
npm run dev

The setup wizard asks about TypeScript, ESLint and the App Router. When you choose the App Router, an app/ folder is created at the project root.

Special Files: page, layout and the Rest

In the App Router, folders create the route, but a segment only becomes truly reachable through files with specific names. The most common ones are:

  • page.tsx — renders the segment's actual content and makes the route publicly accessible.
  • layout.tsx — a shell shared across child segments that is not re-rendered on navigation.
  • loading.tsx — creates a Suspense boundary; the fallback shown while data loads.
  • error.tsx — a client-side component that catches errors within the segment.
  • not-found.tsx — the 404 UI for missing content.

At its simplest, a home page looks like this:

// app/page.tsx
export default function Home() {
  return <h1>Hello aslain.dev</h1>;
}

The Root Layout: a Required Skeleton

app/layout.tsx is required in every App Router project because it defines the <html> and <body> tags. The root layout is the outermost shell wrapping every page, and it is preserved across page transitions:

// app/layout.tsx
export const metadata = {
  title: "aslain.dev",
  description: "A versatile developer's portfolio",
};

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

The children prop represents the child page or inner layout that will be slotted inside this layout. This is the cornerstone of the nested structure.

How Nested Routing Works

Nested routes are created by placing folders inside folders. Say we're building a blog section:

app/
  layout.tsx          → root layout
  page.tsx            → /
  blog/
    layout.tsx        → wraps /blog and below
    page.tsx          → /blog
    [slug]/
      page.tsx        → /blog/any-post

Here [slug] is a dynamic segment. The square brackets say that this part comes from the URL. You access the value through params:

// app/blog/[slug]/page.tsx
export default async function Post({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return <article>Post: {slug}</article>;
}

As of Next.js 15, params is now a Promise and must be awaited, which is why we declared the component as async. The key point: blog/layout.tsx wraps both /blog and /blog/[slug], yet it is not re-rendered as the user moves from post to post. This is ideal for preserving shared elements like a sidebar or header.

Server and Client Components

In the App Router, every component is a Server Component unless stated otherwise. Where you need interactivity (clicks, state, useState, useEffect), you add the "use client" directive at the very top of the file:

// app/components/Counter.tsx
"use client";
import { useState } from "react";

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

A good strategy is to keep most of the tree as Server Components and pull only the leaf-level interactive pieces into the client. This keeps the amount of JavaScript shipped to the browser to a minimum.

Navigating Between Pages

To move between pages, use the next/link component instead of a plain <a>. It provides client-side navigation and prefetching:

import Link from "next/link";

export default function Menu() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/blog">Blog</Link>
    </nav>
  );
}

When you need programmatic navigation, grab the useRouter hook from next/navigation inside client components.

Frequently Asked Questions

Can the App Router and the Pages Router coexist in one project?

Yes. Next.js allows both app/ and pages/ directories at the root level; this is designed for migrating existing projects incrementally. If the same route is defined in both, app/ takes precedence.

What happens if I add a folder without a page.tsx?

A folder without a page.tsx is not reachable by URL; it is used purely for organization (for example route groups or shared components). An accessible route always requires a page.tsx.

Where should I fetch data?

You can use async/await with fetch directly inside Server Components; there is no need for special functions like getServerSideProps. Next.js automatically caches fetch calls and revalidates them when needed.

Want to set up your Next.js project correctly or migrate an existing app to the App Router? I can help you build a clean, performant and SEO-friendly architecture. Get in touch and let's plan your project together.

Bu kategorideki tüm yazılar →

Devamı için