The nextjs app router tutorial gap is real: official docs are reference-heavy, and beginners still think in Pages Router (pages/index.tsx). App Router uses folders in app/ where the file name tells Next.js what to do. I migrated client sites in 2024–2025 and teach this sequence on my YouTube channel. Follow these steps in order — layout, page, loading, then server fetch.
Mental model — folders are routes
Every folder under app/ maps to a URL segment. Special files: page.tsx = UI, layout.tsx = shared shell, loading.tsx = skeleton while slow segments load.
app/
layout.tsx → wraps ALL routes
page.tsx → URL: /
about/
page.tsx → URL: /about
blog/
page.tsx → URL: /blog
[slug]/
page.tsx → URL: /blog/my-post
No React Router install. Rename a folder, you rename a route. That is the core App Router win.
If you are coming from Create React App, delete the mental model of a single App.tsx with a Switch. You will have multiple layout.tsx files at different depths — root for html/body, nested for dashboard sidebars. Each layout wraps only its subtree.
Step 1 — Root layout (HTML shell once)
// app/layout.tsx
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "My App",
description: "Beginner App Router site",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className="min-h-screen bg-white text-neutral-900">
<header className="border-b p-4">My Site</header>
<main>{children}</main>
</body>
</html>
);
}
Layouts do not remount when navigating between child pages — perfect for nav bars and fonts. Keep providers that must persist here (theme, analytics).
Step 2 — First page
// app/page.tsx — home route
export default function HomePage() {
return (
<section className="p-8">
<h1 className="text-3xl font-bold">Welcome</h1>
<p>Your first App Router page — no use client needed.</p>
</section>
);
}
Default export must be a component. This is a Server Component unless you add "use client" at the top.
Step 3 — loading.tsx for perceived speed
// app/blog/loading.tsx — shows while blog segment loads
export default function BlogLoading() {
return (
<div className="animate-pulse space-y-4 p-8">
<div className="h-8 w-48 rounded bg-neutral-200" />
<div className="h-4 w-full rounded bg-neutral-200" />
<div className="h-4 w-3/4 rounded bg-neutral-200" />
</div>
);
}
Next.js wraps the segment in Suspense automatically. Users see skeletons instead of frozen UI — critical on Indian mobile networks.
Step 4 — Server Component fetch (no useEffect)
// app/blog/page.tsx
type Post = { slug: string; title: string };
async function getPosts(): Promise<Post[]> {
const res = await fetch("https://api.example.com/posts", {
next: { revalidate: 3600 },
});
if (!res.ok) throw new Error("Failed to load posts");
return res.json();
}
export default async function BlogPage() {
const posts = await getPosts();
return (
<ul className="space-y-2 p-8">
{posts.map((post) => (
<li key={post.slug}>
<a href={"/blog/" + post.slug}>{post.title}</a>
</li>
))}
</ul>
);
}
// BEFORE — Pages Router habit (client fetch)
"use client";
useEffect(() => fetch("/api/posts").then(/* ... */), []);
// AFTER — async server component
// Data ready before HTML ships — better SEO and LCP
Deeper caching rules: SSR vs SSG vs ISR. Performance tuning: 60% load time case study.
App Router files — quick comparison table
| File | Purpose | Required? |
|---|---|---|
| layout.tsx | Shared UI wrapper | Root required |
| page.tsx | Route UI | Yes per route |
| loading.tsx | Loading skeleton | Optional |
| error.tsx | Error boundary | Optional |
| not-found.tsx | 404 UI | Optional |
| route.ts | API endpoint | Optional |
Step 5 — Add client components only as leaves
// components/CounterButton.tsx
"use client";
import { useState } from "react";
export function CounterButton() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>Clicked {n} times</button>;
}
// app/page.tsx — server page imports client leaf
import { CounterButton } from "@/components/CounterButton";
export default function HomePage() {
return (
<section>
<h1>Home</h1>
<CounterButton />
</section>
);
}
Read RSC vs client components before marking whole pages as client — that is the beginner mistake that balloons bundle size.
Step 6 — Dynamic routes and params
// app/blog/[slug]/page.tsx
type Props = { params: Promise<{ slug: string }> };
export default async function PostPage({ params }: Props) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) return <p>Not found</p>;
return <article><h1>{post.title}</h1></article>;
}
In Next.js 15, params is a Promise — await it. TypeScript strict mode helps catch forgotten awaits — strict mode guide.
Learning path after this guide
Week 1: layouts + pages + loading. Week 2: server fetch + one dynamic route. Week 3: one client form with Server Action. Week 4: deploy to Vercel and run Lighthouse. If you are choosing between stacks first, read Next.js vs React. Organise folders early with project structure guide.
For AI-assisted coding, see Cursor + Claude workflow — but build this hello-world tree by hand once so file conventions stick.
My production setup
In production I scaffold with root layout, route groups for marketing vs app, loading.tsx on slow segments, and async server pages for public content. This portfolio follows the same pattern — thin app/blog/.../page.tsx files, heavy logic elsewhere.
At my day job, beginners who complete these six steps ship their first internal page in a week — faster than learning Pages Router and relearning later.
The single takeaway
App Router = folders + special files. Layout once, page per route, loading for UX, async server fetch for data. Client components are seasoning, not the main dish.
Related: React 19 features. Contact.
If this helped you
I publish free tutorials and write-ups like this in my spare time — no paywall on the guides. If it saved you an afternoon of trial and error, you can support the work:
- → Buy me a coffee at buymeacoffee.com/safdarali
- 👉 Subscribe to my YouTube channel — it's free; 70+ React & Next.js tutorials
Related reading
More guides on safdarali.in — same author, production-focused.
-
Free Video Thumbnail Generator Online — No Upload
Free video thumbnail generator online — extract frames from video without upload. Browser-based, no watermark, YouTube thumbnail from MP4 workflow.
Jun 2026 · Read article →
-
Why Your Next.js 15 App is Still Slow (And How to Fix the React 19 Hydration Lag)
Next.js 15 performance optimization — fix INP, LCP layout shifts, React 19 hydration errors, and React Compiler gaps with a production DevTools workflow.
May 2026 · Read article →
Top comments (0)