Next.js has established itself as the leading React framework for building production-ready, full-stack web applications. Created and maintained by Vercel, it offers an exceptional developer experience with powerful features like server-side rendering (SSR), static site generation (SSG), and integrated API routes. This guide will walk you through the core concepts and practical steps to start building with Next.js.
Why Choose Next.js?
Before diving into implementation, understanding Next.js's value proposition is crucial. It solves several pain points present in client-side only React applications:
- Improved Performance & SEO: Automatic server-side rendering and static generation ensure content is immediately available to search engines and users, reducing time-to-first-byte (TTFB) and largest contentful paint (LCP).
- Simplified Routing: File-based routing eliminates the need for complex routing configuration; simply create files in the
apporpagesdirectory. - Built-in Optimizations: Automatic image optimization, font optimization, script loading strategies, and code splitting are handled out-of-the-box.
- Full-Stack Capabilities: API routes allow you to build backend endpoints alongside your frontend code, simplifying deployment and architecture.
- Flexible Rendering: Choose per-page between SSR, SSG, Incremental Static Regeneration (ISR), and client-side rendering based on your data requirements.
Setting Up Your Next.js Project
Getting started is straightforward. Ensure you have Node.js version 18.17 or later installed. Use the official create-next-app CLI to scaffold a new project with recommended defaults.
-
Run the creation command:
npx create-next-app@latest my-next-app -
Follow the interactive prompts: You'll be asked about using TypeScript, ESLint, Tailwind CSS, and the
src/directory. For this guide, we'll select TypeScript and Tailwind CSS. -
Navigate to your project and start the dev server:
cd my-next-app npm run devVisit
http://localhost:3000to see your running application.
The project structure is now set up with key configuration files (next.config.js, tsconfig.json) and the core app directory for the new App Router.
Understanding the App Router (Recommended)
Next.js 13 introduced the App Router (app/ directory), which is now the recommended routing paradigm. It uses a file-system based router where folders define routes and special files define UI for each segment.
Key Files in the App Router:
layout.tsx: Defines a shared UI layout that persists across navigation. It wraps the childpage.tsx.page.tsx: Defines the unique UI for a route and makes it publicly accessible.loading.tsx: Provides an instant loading UI (suspense boundary) for the route segment.error.tsx: Provides an error boundary for the route segment.template.tsx: Similar tolayout.tsxbut creates a new instance on every navigation.
Example Directory Structure:
app/
├── layout.tsx // Root layout (required)
├── page.tsx // Home page (mapped to "/")
├── about/
│ └── page.tsx // About page (mapped to "/about")
└── blog/
├── layout.tsx // Blog-specific layout
└── [slug]/
└── page.tsx // Dynamic route for blog posts
Creating Pages and Layouts
Let's create a simple homepage with a shared header. Modify the root app/layout.tsx to include a navigation bar.
// app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'My Next.js App',
description: 'A demonstration site built with Next.js',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<nav className="flex items-center justify-between p-4 bg-blue-600 text-white">
<span className="font-bold text-xl">NextApp</span>
<div className="flex gap-6">
<a href="/" className="hover:underline">Home</a>
<a href="/about" className="hover:underline">About</a>
<a href="/blog" className="hover:underline">Blog</a>
</div>
</nav>
<main className="p-8">
{children}
</main>
</body>
</html>
);
}Now, create the home page component:
// app/page.tsx
export default function HomePage() {
return (
<div>
<h1 className="text-3xl font-bold mb-4">Welcome to Next.js</h1>
<p className="text-lg text-gray-700">
This is a full-stack React application with built-in SSR, SSG, and API routes.
</p>
</div>
);
}Data Fetching Strategies
Next.js extends the native fetch API to provide powerful data caching and revalidation capabilities. The method you use depends on your data's nature.
1. Server Components (Default in App Router)
In the App Router, all components are React Server Components by default. You can directly async/await fetch data in the component.
// app/blog/[slug]/page.tsx
interface Post {
id: string;
title: string;
content: string;
}
export default async function BlogPost({ params }: { params: { slug: string } }) {
// This fetch happens on the server at request time (SSR) or at build time (SSG)
// depending on the route configuration.
const response = await fetch(`https://api.example.com/posts/${params.slug}`);
const post: Post = await response.json();
return (
<article>
<h1 className="text-2xl font-bold">{post.title}</h1>
<div className="mt-4 whitespace-pre-line">{post.content}</div>
</article>
);
}2. Static Data with generateStaticParams
For static generation of dynamic routes, export a generateStaticParams function to pre-render paths at build time.
// app/blog/[slug]/page.tsx (updated)
export async function generateStaticParams() {
const response = await fetch('https://api.example.com/posts');
const posts: Post[] = await response.json();
return posts.map((post) => ({
slug: post.id,
}));
}3. Incremental Static Regeneration (ISR)
Combine static generation with the ability to update pages after deployment using revalidate in fetch or generateStaticParams.
const response = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 }, // Revalidate every hour
});Building API Routes
The App Router uses Route Handlers for API endpoints. Create a route.ts file inside a route segment.
// app/api/posts/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const posts = [
{ id: '1', title: 'First Post', excerpt: 'Hello world' },
{ id: '2', title: 'Second Post', excerpt: 'Next.js is great' },
];
return NextResponse.json(posts);
}
export async function POST(request: Request) {
const body = await request.json();
// In a real app, you would validate and save to a database
console.log('Received new post:', body);
return NextResponse.json({ message: 'Post created' }, { status: 201 });
}You can now fetch this data from a Server Component or a Client Component using standard fetch('/api/posts').
Client-Side Interactivity
Not everything can or should be rendered on the server. For interactive UI, you must use Client Components by adding the 'use client'; directive at the top of the file.
// app/components/Counter.tsx
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div className="p-4 border rounded">
<p className="mb-2">Count: {count}</p>
<button
onClick={() => setCount(c => c + 1)}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Increment
</button>
</div>
);
}Import this Counter into any Server Component, and it will work seamlessly. Next.js handles the hydration automatically.
Deployment and Optimization
When you're ready to deploy, Next.js offers several options:
- Vercel: The optimal platform, with zero-configuration deployment, preview environments, and analytics.
- Docker: Suitable for self-hosted environments. Build a production image with
npm run buildand start it withnpm run start. - Static Export: Use
next exportfor fully static sites (no server-side features).
Key Optimization Tips:
- Use the
<Image>component fromnext/imagefor automatic image optimization. - Use the
<Link>component fromnext/linkfor client-side navigation and prefetching. - Implement dynamic imports (
next/dynamic) for heavy components to reduce initial bundle size. - Utilize built-in SEO with the
metadataexport inlayout.tsxorpage.tsx.



