WritingNext.js App Router: A Beginner's Guide to the Core Concepts — LinkIT
5 min readnext.js, app-router, beginners, routing, getting-started

Next.js App Router: A Beginner's Guide to the Core Concepts

A clear beginner's introduction to the Next.js App Router — what it is, how file-based routing works, and the mental model you need before writing your first page.

The Next.js App Router combines file-based routing with React Server Components. Understanding how routes, layouts, and component boundaries fit together makes the file conventions easier to apply.

Start with the directory structure, then follow how a page loads data and adds interactivity.

What the App Router Is

The App Router is a React application framework built on top of React Server Components. It handles routing through a file system convention, and it renders pages by mixing server and client components together — with server components as the default.

The key shift from the Pages Router: in the Pages Router, you exported React components from files and Next.js rendered them on the server for you. In the App Router, React Server Components are the runtime, and the routing conventions are the way you organize them.

The app Directory

All App Router code lives in the app directory at the project root. The folder structure defines your URL structure.

app/
  page.tsx          → /
  about/
    page.tsx        → /about
  blog/
    page.tsx        → /blog
    [slug]/
      page.tsx      → /blog/any-slug-here

Square brackets in folder names create dynamic segments. The value inside the brackets becomes a parameter your page can read.

The Special Files

Five file names have meaning in the App Router. Understanding what each one does is the foundation of everything else.

page.tsx — Defines the page UI for a route. App Router pages also support .js and .jsx; route.ts or route.js defines an HTTP handler. Other colocated components and utilities do not become public endpoints merely by being in the folder.

layout.tsx — This is a wrapper that wraps the page and stays mounted as users navigate between routes in the same section. A layout at app/dashboard/layout.tsx wraps every page under /dashboard/*. The root layout at app/layout.tsx wraps everything, including the html and body tags.

loading.tsx — This shows while the page's data is loading. Next.js wraps the page in a Suspense boundary automatically when this file exists. You see the loading state immediately and the real page when data is ready.

error.tsx — This shows when the page throws an error. It replaces the broken page with a recovery UI. You can read the error and offer a retry button.

not-found.tsx — This shows when you call notFound() from within a page, or when no route matches a URL.

Each of these files can exist at any level of the folder hierarchy. Layouts, loading states, and error boundaries nest and apply only to their segment and its children.

Pages Are Server Components by Default

This is the key concept that trips up most beginners. When you write a page.tsx file, it is a React Server Component unless you explicitly mark it otherwise. That means:

  • It runs on the server, not in the browser
  • It can await database queries or API calls directly in the component body
  • It never ships its own JavaScript to the client
  • It cannot use useState, useEffect, or any browser API

For interactivity, such as a controlled form or menu, add a Client Component with 'use client'. Its imported client dependencies also contribute to browser JavaScript; keep that boundary focused on the interactive UI.

A Simple Page in Practice

The example below uses the synchronous params form from older Next.js versions. In current App Router code, params is a promise: await it before reading the slug. Next.js page reference.

// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'
import { getPostBySlug } from '@/lib/db'
 
export default async function BlogPostPage({
  params,
}: {
  params: { slug: string }
}) {
  const post = await getPostBySlug(params.slug)
 
  if (!post) notFound()
 
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.publishedAt}</p>
      <div>{post.content}</div>
    </article>
  )
}

The data query runs on the server. Next.js sends the rendered result and RSC payload; framework JavaScript still supports navigation and any Client Components.

Use Link from next/link for internal navigation. Prefetching depends on route type, configuration, and production behavior; the rendered link also supports ordinary browser navigation.

import Link from 'next/link'
 
export function NavBar() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/blog">Blog</Link>
    </nav>
  )
}

For programmatic navigation — redirecting after a form submission — use redirect() from next/navigation inside a server component or Server Action, or useRouter() inside a client component.

Route Groups

Sometimes you want shared layouts for a group of routes without affecting the URL. Route groups are folders wrapped in parentheses:

app/
  (marketing)/
    layout.tsx     ← shared marketing layout
    page.tsx       → /
    about/
      page.tsx     → /about
  (dashboard)/
    layout.tsx     ← shared dashboard layout
    settings/
      page.tsx     → /settings

The (marketing) and (dashboard) folder names do not appear in the URL. They exist purely to organize layouts.

What to Learn Next

Once the mental model clicks, the natural next steps are:

  1. Data fetching patterns — parallel fetches, Suspense streaming, caching
  2. Server Actions — form submissions and mutations
  3. Proxy (called Middleware before Next.js 16) — request handling such as redirects; enforce authorization again where data is accessed
  4. Metadata API — page titles and descriptions

Keep server and client responsibilities clear as you add data fetching, forms, and navigation.

Discuss a Next.js application with LinkIT.