1. What is Next.js?
Next.js is a React framework for production, providing SSR, SSG, file-based routing, API routes, and many other features out-of-the-box. Developed by Vercel.
| Features | React (Vite) | Next.js |
|---|---|---|
| Rendering | Client-side only | SSR, SSG, ISR, Streaming |
| Routing | React Router (manual) | File-based (built-in) |
| SEO | Poor (SPA) | Excellent (SSR/SSG) |
| API | Separate backend | Route Handlers (built-in) |
| Image/Font | Manual optimization | next/image, next/font |
| Deployment | Any static host | Vercel, Docker, any Node.js host |
2. Setup
npx create-next-app@latest my-app --typescript --tailwind --app --src-dir
cd my-app
npm run dev
3. App Router — File-based Routing
src/app/
├── layout.tsx ← Root layout (required)
├── page.tsx ← Home page (/)
├── loading.tsx ← Loading UI
├── error.tsx ← Error UI
├── not-found.tsx ← 404 page
├── globals.css
│
├── about/
│ └── page.tsx ← /about
│
├── blog/
│ ├── page.tsx ← /blog
│ └── [slug]/
│ └── page.tsx ← /blog/:slug (dynamic)
│
├── dashboard/
│ ├── layout.tsx ← Nested layout
│ ├── page.tsx ← /dashboard
│ ├── settings/
│ │ └── page.tsx ← /dashboard/settings
│ └── [...catchAll]/
│ └── page.tsx ← /dashboard/anything/else
│
└── (marketing)/ ← Route group (no URL segment)
├── layout.tsx
├── pricing/
│ └── page.tsx ← /pricing
└── features/
└── page.tsx ← /features
4. Layouts
// src/app/layout.tsx — Root Layout
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: { default: 'My App', template: '%s | My App' },
description: 'My awesome Next.js application',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="vi">
<body className={inter.className}>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
// src/app/dashboard/layout.tsx — Nested Layout
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex">
<Sidebar />
<div className="flex-1">{children}</div>
</div>
);
}
5. Pages
// src/app/page.tsx — Home page
export default function HomePage() {
return <h1>Welcome to My App</h1>;
}
// src/app/blog/[slug]/page.tsx — Dynamic route
interface Props {
params: Promise<{ slug: string }>;
}
export default async function BlogPost({ params }: Props) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound(); // → not-found.tsx
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
// Generate static params for SSG
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map(post => ({ slug: post.slug }));
}
6. Loading & Error UI
// src/app/dashboard/loading.tsx
export default function Loading() {
return <div className="animate-pulse">Loading dashboard...</div>;
}
// src/app/dashboard/error.tsx
'use client'; // Error components must be Client Components
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
7. Navigation
import Link from 'next/link';
import { useRouter, usePathname } from 'next/navigation';
// Link component (prefetching built-in)
<Link href="/about">About</Link>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
<Link href="/dashboard" prefetch={false}>Dashboard</Link>
// Programmatic navigation
'use client';
function SearchForm() {
const router = useRouter();
const pathname = usePathname();
const handleSubmit = (query: string) => {
router.push(`/search?q=${query}`);
// router.replace('/dashboard'); // Replace history
// router.back(); // Go back
// router.refresh(); // Re-fetch server components
};
}
Next article: Server Components & Client Components — RSC, "use client", boundaries, and composition patterns.