Skip to content

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

bash
# 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.

jsx
// 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)

jsx
// 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)

jsx
// 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)

jsx
// 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

jsx
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.

jsx
// 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

jsx
// 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

jsx
// 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)

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.

jsx
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.

jsx
// 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.

jsx
// 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.

jsx
// 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

bash
# 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
jsx
// 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

jsx
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.


This document will be continuously updated. If you have any questions, please provide feedback through GitHub Issues.

VitePress Development Guide