Built and maintained by Vercel, Next.js has become the go-to framework for building modern React applications, especially when you need server-side capabilities or optimized performance.
Next.js provides: automatic code splitting, built-in CSS/Sass support, API routes, server-side rendering, static generation, image optimization, and seamless Vercel deployment. It’s the framework that takes React to production level.
Next.js vs Create React App
While Create React App (CRA) is great for client-side React applications, Next.js goes further:
| Feature | Create React App | Next.js |
|---|---|---|
| Server-Side Rendering | No | Yes |
| Static Generation | No | Yes |
| API Routes | No | Yes |
| Image Optimization | Manual | Built-in |
| Code Splitting | Manual | Automatic |
| Routing | Requires react-router | File-based |
Getting Started with Next.js
Installation
Create a new Next.js project with one command:
npx create-next-app@latest my-app
cd my-app
npm run dev
Your app will be running at http://localhost:3000
Project Structure
├── app/
│ ├── layout.js
│ ├── page.js
│ └── globals.css
├── public/
├── package.json
└── next.config.js
Your First Page
export default function Home() {
return (
<main>
<h1>Welcome to Next.js!</h1>
<p>Get started by editing app/page.js</p>
</main>
);
}
File-Based Routing System
Next.js uses file-based routing, where the file structure automatically creates routes. No configuration needed!
Routing Examples
| File Location | Route URL | Description |
|---|---|---|
| app/page.js | / | Homepage |
| app/about/page.js | /about | About page |
| app/posts/[id]/page.js | /posts/123 | Dynamic route |
Creating a Dynamic Route
export default function Post({ params }) {
return (
<article>
<h1>Post ID: {params.id}</h1>
<p>This is the post with ID {params.id}</p>
</article>
);
}
Navigation Between Pages
return (
<nav>
<Link href=”/”>Home</Link>
<Link href=”/about”>About</Link>
<Link href=”/posts/1″>Post 1</Link>
</nav>
);
}
Server-Side Rendering (SSR)
Server-Side Rendering generates the page on every request. The server renders React components and sends HTML to the browser. This is perfect for dynamic content that changes frequently.
How SSR Works
- User requests a page
- Server renders the React component
- Server sends fully rendered HTML to browser
- Browser displays the content immediately (great for SEO)
Getting Server-Side Data
export default async function UsersPage() {
const res = await fetch(‘https://api.example.com/users’);
const users = await res.json();return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
By default, components are Server Components. Mark with ‘use client’ for interactive components.
When to Use SSR
- Content changes frequently (every request)
- Personalized content for each user
- Need to access user authentication data
- Real-time data updates
Static Site Generation (SSG)
Static Generation builds pages at build time, creating static HTML files. These pages are served instantly from a CDN. Perfect for content that doesn’t change often.
How SSG Works
- At build time, Next.js renders all pages
- Creates static HTML files
- Serves same HTML to all users
- Incredibly fast (served from CDN globally)
Static Generation with Data
export async function generateStaticParams() {
const res = await fetch(‘https://api.example.com/posts’);
const posts = await res.json();return posts.map(post => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }) {
const res = await fetch(
‘https://api.example.com/posts/’ + params.slug
);
const post = await res.json();
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
Incremental Static Regeneration (ISR)
ISR is the best of both worlds: static performance with dynamic content. Pages are revalidated in the background at specified intervals.
Implementing ISR
export const revalidate = 3600; // Revalidate every hourexport default async function Product({ params }) {
const product = await fetch(
‘https://api.example.com/products/’ + params.id
).then(r => r.json());
return (
<div>
<h1>{product.name}</h1>
<p>Price: {product.price}</p>
</div>
);
}
Revalidation options:
- revalidate = 60 – Revalidate every 60 seconds
- revalidate = false – Never revalidate
- revalidate = 0 – Revalidate on every request
Building API Routes
Next.js allows you to build a complete REST API within your application. Create backend endpoints without needing a separate server.
Creating an API Route
export async function GET(request) {
return new Response(
JSON.stringify({ users: [] }),
{ headers: { ‘Content-Type’: ‘application/json’ } }
);
}export async function POST(request) {
const body = await request.json();
return new Response(
JSON.stringify({ success: true, data: body }),
{ status: 201 }
);
}
Dynamic API Routes
export async function GET(request, { params }) {
const userId = params.id;
return new Response(
JSON.stringify({ id: userId, name: ‘User’ })
);
}
Deployment to Vercel
Vercel, the creators of Next.js, provides the optimal hosting platform. Deployment is incredibly simple.
Deploy in 3 Steps
- Push your code to GitHub
- Connect your GitHub repo to Vercel (at vercel.com)
- Vercel automatically deploys on every push
Environment Variables
Create a .env.local file for sensitive data:
API_SECRET=your_secret_key
NEXT_PUBLIC_API_URL=https://api.example.com
Next.js Best Practices
1. Use Server Components by Default
Keep components as Server Components for better performance. Only use ‘use client’ when you need interactivity.
2. Optimize Images
Use Next.js Image component for automatic optimization:
src=”/image.jpg”
alt=”Description”
width={800}
height={600}
priority
/>
3. Implement Proper Loading States
<SlowComponent />
</Suspense>
4. Use Middleware for Authentication
export function middleware(request) {
const token = request.cookies.get(‘token’);if (!token && request.nextUrl.pathname.startsWith(‘/admin’)) {
return NextResponse.redirect(
new URL(‘/login’, request.url)
);
}
}
export const config = {
matcher: [‘/admin/:path*’],
};