Next.js
A comprehensive guide to Next.js, the React framework for production.
Introduction to Next.js
Next.js is a React framework that enables functionality such as server-side rendering, static site generation, API routes, and more. It provides a great developer experience with features like fast refresh and zero configuration.
Key Features of Next.js
- Hybrid Rendering: Server-side rendering (SSR), static site generation (SSG), and incremental static regeneration (ISR)
- File-based Routing: Automatic routing based on your file structure
- API Routes: Create API endpoints as part of your Next.js application
- Built-in CSS Support: Support for CSS Modules, Sass, and other styling solutions
- Image Optimization: Automatic image optimization with the Image component
- Zero Config: Works out of the box with sensible defaults
- TypeScript Support: Built-in TypeScript support
- Fast Refresh: Instant feedback during development
Getting Started with Next.js
Prerequisites
Before starting with Next.js, you should have:
- Node.js 14.6.0 or newer
- Basic knowledge of JavaScript and React
- A code editor (VS Code recommended)
Creating a Next.js Project
# Create a new Next.js project
npx create-next-app@latest my-next-app
# Navigate to the project directory
cd my-next-app
# Start the development server
npm run dev
Project Structure
A typical Next.js project structure looks like this:
my-next-app/
├── .next/ # Build output (auto-generated)
├── node_modules/ # Node.js dependencies
├── pages/ # Page components and API routes
│ ├── api/ # API routes
│ ├── _app.js # Custom App component
│ ├── _document.js # Custom Document component
│ └── index.js # Homepage
├── public/ # Static assets
├── styles/ # CSS files
├── components/ # React components
├── lib/ # Utility functions
├── .eslintrc.json # ESLint configuration
├── next.config.js # Next.js configuration
├── package.json # Project dependencies and scripts
└── README.md # Project documentation
Core Concepts
1. Pages and Routing
Next.js has a file-system based router built on the concept of pages. When a file is added to the pages
directory, it's automatically available as a route.
// pages/index.js - accessible at /
export default function Home() {
return <h1>Hello, Next.js!</h1>;
}
// pages/about.js - accessible at /about
export default function About() {
return <h1>About Us</h1>;
}
// pages/posts/[id].js - accessible at /posts/1, /posts/2, etc.
import { useRouter } from 'next/router';
export default function Post() {
const router = useRouter();
const { id } = router.query;
return <h1>Post: {id}</h1>;
}
2. Data Fetching
Next.js provides several ways to fetch data for your pages:
getStaticProps (Static Generation)
// This function runs at build time in production
export async function getStaticProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return {
props: { data }, // Will be passed to the page component as props
revalidate: 60, // Optional: regenerate page after 60 seconds
};
}
export default function Page({ data }) {
return <div>{data.title}</div>;
}
getStaticPaths (Static Generation with Dynamic Routes)
// This function runs at build time in production
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
// Generate paths for each post
const paths = posts.map((post) => ({
params: { id: post.id.toString() },
}));
return {
paths,
fallback: 'blocking', // or true or false
};
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.id}`);
const post = await res.json();
return {
props: { post },
};
}
export default function Post({ post }) {
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
getServerSideProps (Server-side Rendering)
// This function runs on every request
export async function getServerSideProps(context) {
const res = await fetch(`https://api.example.com/data?q=${context.query.q}`);
const data = await res.json();
return {
props: { data }, // Will be passed to the page component as props
};
}
export default function Page({ data }) {
return <div>{data.title}</div>;
}
Client-side Data Fetching
import { useState, useEffect } from 'react';
export default function Page() {
const [data, setData] = useState(null);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
fetch('https://api.example.com/data')
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
});
}, []);
if (isLoading) return <p>Loading...</p>;
if (!data) return <p>No data</p>;
return (
<div>
<h1>{data.title}</h1>
<p>{data.description}</p>
</div>
);
}
3. API Routes
Next.js allows you to create API endpoints as part of your application by adding files to the pages/api
directory.
// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' });
}
// pages/api/users/[id].js
export default function userHandler(req, res) {
const { id } = req.query;
const { method } = req;
switch (method) {
case 'GET':
// Get user data from your database
res.status(200).json({ id, name: `User ${id}` });
break;
case 'PUT':
// Update user data in your database
res.status(200).json({ id, name: req.body.name });
break;
default:
res.setHeader('Allow', ['GET', 'PUT']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
4. Styling
Next.js supports various styling methods:
CSS Modules
// styles/Home.module.css
.container {
padding: 0 2rem;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
color: #0070f3;
}
// pages/index.js
import styles from '../styles/Home.module.css';
export default function Home() {
return (
<div className={styles.container}>
<h1 className={styles.title}>Welcome to Next.js!</h1>
</div>
);
}
Global CSS
// styles/globals.css
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
// pages/_app.js
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default MyApp;
CSS-in-JS (Styled JSX)
export default function Button() {
return (
<div>
<button>Click me</button>
<style jsx>{`
button {
background: #0070f3;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0051a2;
}
`}</style>
</div>
);
}
5. Image Optimization
Next.js provides an Image component that optimizes images for performance.
import Image from 'next/image';
export default function Home() {
return (
<div>
<h1>My Homepage</h1>
<Image
src="/profile.jpg"
alt="Profile Picture"
width={500}
height={300}
priority
/>
<p>Welcome to my website!</p>
</div>
);
}
Advanced Features
1. Middleware
Middleware allows you to run code before a request is completed.
// middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
// Check if user is authenticated
const isAuthenticated = checkAuth(request);
if (!isAuthenticated) {
// Redirect to login page if not authenticated
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: '/dashboard/:path*',
};
2. Internationalization (i18n)
Next.js supports internationalized routing and content.
// next.config.js
module.exports = {
i18n: {
locales: ['en', 'fr', 'de'],
defaultLocale: 'en',
},
};
// pages/index.js
import { useRouter } from 'next/router';
export default function Home() {
const router = useRouter();
const { locale } = router;
return (
<div>
<h1>{locale === 'en' ? 'Welcome' : locale === 'fr' ? 'Bienvenue' : 'Willkommen'}</h1>
<button onClick={() => router.push('/', '/', { locale: 'en' })}>English</button>
<button onClick={() => router.push('/', '/', { locale: 'fr' })}>Français</button>
<button onClick={() => router.push('/', '/', { locale: 'de' })}>Deutsch</button>
</div>
);
}
3. Authentication
Next.js can be integrated with authentication providers like NextAuth.js.
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
export default NextAuth({
providers: [
Providers.GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
],
database: process.env.DATABASE_URL,
session: {
jwt: true,
},
callbacks: {
async session(session, user) {
session.user.id = user.id;
return session;
},
},
});
// pages/profile.js
import { useSession, signIn, signOut } from 'next-auth/react';
export default function Profile() {
const { data: session } = useSession();
if (!session) {
return (
<div>
<p>You are not signed in</p>
<button onClick={() => signIn()}>Sign in</button>
</div>
);
}
return (
<div>
<p>Welcome, {session.user.name}!</p>
<button onClick={() => signOut()}>Sign out</button>
</div>
);
}
Deployment
Vercel (Recommended)
# Install Vercel CLI
npm install -g vercel
# Deploy
vercel
Other Platforms
Next.js can be deployed to any platform that supports Node.js, such as:
- AWS Amplify
- Netlify
- DigitalOcean App Platform
- Google Cloud Run
- Heroku
Best Practices
1. Code Organization
- Keep pages simple and focused on rendering
- Extract business logic into separate files
- Use a consistent folder structure
- Group related components and utilities
2. Performance Optimization
- Use appropriate data fetching methods for each use case
- Implement code splitting with dynamic imports
- Optimize images with the Next.js Image component
- Minimize client-side JavaScript
- Use the built-in performance analytics
// Dynamic import example
import dynamic from 'next/dynamic';
const DynamicComponent = dynamic(() => import('../components/heavy-component'), {
loading: () => <p>Loading...</p>,
ssr: false, // Disable server-side rendering
});
export default function Page() {
return (
<div>
<h1>My Page</h1>
<DynamicComponent />
</div>
);
}
3. SEO Best Practices
import Head from 'next/head';
export default function Page({ product }) {
return (
<div>
<Head>
<title>{product.name} | My Store</title>
<meta name="description" content={product.description} />
<meta property="og:title" content={product.name} />
<meta property="og:description" content={product.description} />
<meta property="og:image" content={product.image} />
<link rel="canonical" href={`https://mystore.com/products/${product.id}`} />
</Head>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
Frequently Asked Questions
How does Next.js differ from Create React App?
Next.js provides server-side rendering, static site generation, and API routes, while Create React App is focused on client-side rendering only.
When should I use getStaticProps vs. getServerSideProps?
- Use
getStaticProps
when the data can be fetched at build time and doesn't change often - Use
getServerSideProps
when the data needs to be fetched on each request or depends on request parameters
How do I handle authentication in Next.js?
You can use libraries like NextAuth.js, Auth0, or Firebase Authentication, or implement your own authentication system using API routes and cookies.
Can I use Redux with Next.js?
Yes, you can use Redux with Next.js. You'll need to set up a Redux store and wrap your application with a provider.
How do I deploy a Next.js application?
The easiest way is to deploy to Vercel, the platform built by the creators of Next.js. You can also deploy to other platforms like Netlify, AWS, or any platform that supports Node.js.
Related Resources
- Next.js Official Documentation
- Next.js GitHub Repository
- Vercel Platform
- Next.js Examples
- More Tutorials
This document will be continuously updated. If you have any questions, please provide feedback through GitHub Issues.