Next.js

1. Introduction to Next.js

Next.js is a popular React framework created by Vercel that enables functionalities such as server-side rendering and static site generation. It simplifies the process of building React applications by providing a comprehensive set of features and optimizations out of the box. Next.js is designed to help you build high-performance web applications quickly and efficiently.

Key Features of Next.js:

  • Server-Side Rendering (SSR): Renders pages on the server at request time.

  • Static Site Generation (SSG): Generates static HTML at build time.

  • API Routes: Allows you to create API endpoints within the same project.

  • Automatic Code Splitting: Only the necessary JavaScript is loaded.

  • File-Based Routing: Pages are automatically routed based on the file structure.

  • Optimized Performance: Built-in optimizations for faster load times.

2. Setting Up a Next.js Project

To get started with Next.js, you need to set up a new project. Here’s how to do it:

Installation:

npx create-next-app@latest my-next-app
cd my-next-app
npm run dev

In this example:

  • npx create-next-app@latest my-next-app creates a new Next.js project in a directory called my-next-app.

  • cd my-next-app navigates into the project directory.

  • npm run dev starts the development server, usually accessible at http://localhost:3000.

3. Project Structure

A new Next.js project comes with a specific structure:

my-next-app/
  ├── node_modules/
  ├── pages/
  │   ├── api/
  │   │   └── hello.js
  │   ├── _app.js
  │   ├── _document.js
  │   ├── index.js
  │   └── about.js
  ├── public/
  ├── styles/
  │   ├── globals.css
  │   └── Home.module.css
  ├── .gitignore
  ├── package.json
  └── README.md

Key Directories and Files:

  • pages/: Contains all the page components. Each file in this directory corresponds to a route in your application.

  • pages/api/: Contains API route files. Each file in this directory is mapped to /api/* and will be treated as an API endpoint.

  • pages/_app.js: Customizes the default App component, allowing you to keep state between pages, inject global CSS, and more.

  • pages/_document.js: Customizes the default Document component, allowing you to modify the server-side rendered document markup.

  • public/: Static files like images, fonts, etc., are placed here and served at the root URL.

  • styles/: Contains global styles and CSS modules.

4. Creating Pages

In Next.js, pages are React components that are stored in the pages directory. Each file in this directory automatically becomes a route.

Example:

// pages/index.js
import React from 'react';
import Link from 'next/link';

const Home = () => (
  <div>
    <h1>Welcome to Next.js!</h1>
    <Link href="/about">
      <a>About</a>
    </Link>
  </div>
);

export default Home;

In this example:

  • The Home component is a functional React component that renders the homepage.

  • The Link component from next/link is used to create client-side navigable links.

5. Static Site Generation (SSG)

Next.js allows you to generate static HTML pages at build time, which can be served to clients very quickly.

Example:

// pages/posts/[id].js
import React from 'react';

const Post = ({ post }) => {
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
};

export async function getStaticPaths() {
  const paths = [
    { params: { id: '1' } },
    { params: { id: '2' } },
  ];
  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const post = {
    id: params.id,
    title: `Post ${params.id}`,
    content: `This is the content for post ${params.id}.`,
  };
  return { props: { post } };
}

export default Post;

In this example:

  • getStaticPaths returns an array of paths to be statically generated.

  • getStaticProps fetches the data for each post at build time.

6. Server-Side Rendering (SSR)

Next.js can render pages on the server at request time, providing dynamic data to each request.

Example:

// pages/profile.js
import React from 'react';

const Profile = ({ user }) => {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.bio}</p>
    </div>
  );
};

export async function getServerSideProps() {
  const res = await fetch('<https://api.example.com/user/1>');
  const user = await res.json();

  return { props: { user } };
}

export default Profile;

In this example:

  • getServerSideProps fetches data on each request, ensuring that the data is always up-to-date.

7. API Routes

Next.js allows you to create API endpoints within the same project, making it a full-stack framework.

Example:

// pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello, World!' });
}

In this example:

  • The handler function responds to requests sent to /api/hello with a JSON message.

9. Advanced Routing

Next.js provides a powerful and flexible routing system based on the file system. This allows you to easily create nested routes, dynamic routes, and catch-all routes.

Nested Routes:

You can create nested routes by creating subdirectories within the pages directory.

Example:

// pages/blog/index.js
import React from 'react';

const Blog = () => (
  <div>
    <h1>Blog Home</h1>
  </div>
);

export default Blog;

// pages/blog/[id].js
import React from 'react';
import { useRouter } from 'next/router';

const BlogPost = () => {
  const router = useRouter();
  const { id } = router.query;

  return (
    <div>
      <h1>Blog Post {id}</h1>
    </div>
  );
};

export default BlogPost;

In this example:

  • The index.js file inside the blog directory will match the /blog route.

  • The [id].js file inside the blog directory will match dynamic routes like /blog/1 or /blog/2.

Catch-All Routes:

Catch-all routes can be created using the [...] syntax, allowing you to match multiple segments of a URL.

Catch-all routes in Next.js allow you to handle multiple URL segments with a single route, creating dynamic pages that respond to various paths easily. These routes capture the URL segments as an array, which you can then use in your page component to display different content based on the URL.

Example:

// pages/docs/[...slug].js
import React from 'react';
import { useRouter } from 'next/router';

const Docs = () => {
  const router = useRouter();
  const { slug } = router.query;

  return (
    <div>
      <h1>Docs</h1>
      <p>Slug: {slug.join('/')}</p>
    </div>
  );
};

export default Docs;

In this example:

  • The [...slug].js file will match routes like /docs/a/b/c and capture the segments in the slug array.

10. API Routes

Next.js allows you to create API endpoints within your application. This can be useful for handling form submissions, authentication, database interactions, and more.

Creating API Routes:

Example:

// pages/api/users.js
export default function handler(req, res) {
  const users = [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Doe' },
  ];

  res.status(200).json(users);
}

In this example:

  • The handler function responds to requests sent to /api/users with a JSON array of users.

Handling POST Requests:

You can handle different HTTP methods in your API routes.

Example:

// pages/api/users.js
export default function handler(req, res) {
  if (req.method === 'POST') {
    // Handle POST request
    const { name } = req.body;
    const newUser = { id: Date.now(), name };

    res.status(201).json(newUser);
  } else {
    // Handle other methods
    res.status(405).end(); // Method Not Allowed
  }
}

In this example:

  • The handler function checks the HTTP method and processes POST requests differently from GET requests.

11. Data Fetching Methods

Next.js provides multiple data fetching methods to suit different needs, including getStaticProps, getStaticPaths, and getServerSideProps.

getStaticProps:

getStaticProps is used to fetch data at build time for static site generation.

Example:

// pages/posts.js
import React from 'react';

const Posts = ({ posts }) => (
  <div>
    <h1>Posts</h1>
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  </div>
);

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  return {
    props: {
      posts,
    },
  };
}

export default Posts;

In this example:

  • getStaticProps fetches data at build time, and the data is passed to the Posts component as props.

getStaticPaths:

getStaticPaths is used with getStaticProps to generate dynamic routes at build time.

Example:

// pages/posts/[id].js
import React from 'react';

const Post = ({ post }) => (
  <div>
    <h1>{post.title}</h1>
    <p>{post.content}</p>
  </div>
);

export async function getStaticPaths() {
  const res = await fetch('<https://api.example.com/posts>');
  const posts = await res.json();

  const paths = posts.map(post => ({
    params: { id: post.id.toString() },
  }));

  return { paths, fallback: 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 Post;

In this example:

  • getStaticPaths generates the paths for all posts.

  • getStaticProps fetches the data for each post at build time.

getServerSideProps:

getServerSideProps is used to fetch data on each request for server-side rendering.

Example:

// pages/dashboard.js
import React from 'react';

const Dashboard = ({ data }) => (
  <div>
    <h1>Dashboard</h1>
    <p>{data.info}</p>
  </div>
);

export async function getServerSideProps() {
  const res = await fetch('<https://api.example.com/dashboard>');
  const data = await res.json();

  return {
    props: {
      data,
    },
  };
}

export default Dashboard;

In this example:

  • getServerSideProps fetches data on each request, ensuring the data is always fresh.

12. Custom Document and App

Next.js allows you to customize the default Document and App components to control the initial HTML markup and provide global state or styles.

Custom Document:

Customizing the Document component allows you to augment the HTML and body tags used to render the application.

Example:

// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link rel="stylesheet" href="<https://example.com/styles.css>" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

In this example:

  • The custom Document component allows you to include external stylesheets and other modifications to the HTML document.

Custom App:

Customizing the App component allows you to inject global styles, state, or context.

Example:

// pages/_app.js
import React from 'react';
import { GlobalProvider } from '../context/GlobalContext';
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
  return (
    <GlobalProvider>
      <Component {...pageProps} />
    </GlobalProvider>
  );
}

export default MyApp;

In this example:

  • The custom App component wraps every page with the GlobalProvider to provide global state and includes global styles.

14. Image Optimization

Next.js provides built-in image optimization, which improves the performance of your website by serving optimized images. The next/image component automatically optimizes images on-demand for each device size.

Example:

// pages/index.js
import React from 'react';
import Image from 'next/image';

const Home = () => (
  <div>
    <h1>Welcome to Next.js!</h1>
    <Image
      src="/images/example.jpg"
      alt="Example Image"
      width={500}
      height={300}
    />
  </div>
);

export default Home;

In this example:

  • The Image component from next/image is used to include an image.

  • The image is automatically optimized for different device sizes and formats.

15. Internationalized Routing

Next.js supports internationalized routing, enabling you to build multilingual websites.

Configuration: Add the i18n configuration to your next.config.js file.

Example:

// next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'fr', 'de'],
    defaultLocale: 'en',
  },
};

In this example:

  • The locales array defines the supported languages.

  • The defaultLocale is the default language for the application.

Using Localized Routes:

Example:

// pages/index.js
import React from 'react';
import { useRouter } from 'next/router';
import Link from 'next/link';

const Home = () => {
  const { locale, locales, defaultLocale } = useRouter();

  return (
    <div>
      <h1>Welcome to Next.js!</h1>
      <p>Current locale: {locale}</p>
      <p>Default locale: {defaultLocale}</p>
      <p>Available locales: {locales.join(', ')}</p>
      <nav>
        <Link href="/" locale="en">
          English
        </Link>
        <Link href="/" locale="fr">
          French
        </Link>
        <Link href="/" locale="de">
          German
        </Link>
      </nav>
    </div>
  );
};

export default Home;

In this example:

  • The useRouter hook is used to access localization information.

  • The Link component includes the locale prop to switch between languages.

16. Static and Server-Side Rendering

Next.js offers two primary rendering strategies: Static Site Generation (SSG) and Server-Side Rendering (SSR). These methods help improve performance and SEO.

Static Site Generation (SSG): SSG generates HTML at build time and reuses it on each request, making the site fast and performant.

Example:

// pages/products.js
import React from 'react';

const Products = ({ products }) => (
  <div>
    <h1>Products</h1>
    <ul>
      {products.map((product) => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  </div>
);

export async function getStaticProps() {
  const res = await fetch('<https://api.example.com/products>');
  const products = await res.json();

  return {
    props: {
      products,
    },
  };
}

export default Products;

Server-Side Rendering (SSR): SSR generates HTML on each request, providing up-to-date data at the cost of slower performance compared to SSG.

Example:

// pages/orders.js
import React from 'react';

const Orders = ({ orders }) => (
  <div>
    <h1>Orders</h1>
    <ul>
      {orders.map((order) => (
        <li key={order.id}>{order.item}</li>
      ))}
    </ul>
  </div>
);

export async function getServerSideProps() {
  const res = await fetch('<https://api.example.com/orders>');
  const orders = await res.json();

  return {
    props: {
      orders,
    },
  };
}

export default Orders;

17. Incremental Static Regeneration (ISR)

ISR allows you to update static content after it has been built. You can define how often the content should be revalidated and updated.

Example:

// pages/blog/[id].js
import React from 'react';

const BlogPost = ({ post }) => (
  <div>
    <h1>{post.title}</h1>
    <p>{post.content}</p>
  </div>
);

export async function getStaticPaths() {
  const res = await fetch('<https://api.example.com/posts>');
  const posts = await res.json();

  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));

  return { paths, fallback: 'blocking' };
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();

  return {
    props: { post },
    revalidate: 10, // Revalidate at most every 10 seconds
  };
}

export default BlogPost;

In this example:

  • The getStaticProps function includes a revalidate property to specify the revalidation interval.

18. API Routes with Dynamic Parameters

Next.js API routes can handle dynamic parameters, making them flexible for various use cases.

Example:

// pages/api/users/[id].js
export default function handler(req, res) {
  const { id } = req.query;
  // Fetch user data from a database or external API
  const user = { id, name: `User ${id}` };

  res.status(200).json(user);
}

In this example:

  • The API route handles requests to /api/users/[id] and returns user data based on the dynamic id parameter.

19. Deploying a Next.js Application

Next.js applications can be deployed on various platforms, including Vercel, which is the creator of Next.js, as well as other platforms like Netlify, AWS, and DigitalOcean.

Deploying on Vercel:

  1. Push your project to a Git repository (GitHub, GitLab, or Bitbucket).

  2. Go to the Vercel website and log in with your Git provider.

  3. Click "New Project" and import your repository.

  4. Follow the setup steps and click "Deploy".

13. Summary

In this segment, we delved into the advanced features of Next.js, such as nested and catch-all routing, API routes, and various data fetching methods (getStaticProps, getStaticPaths, getServerSideProps). Additionally, we covered how to customize the Document and App components. These capabilities significantly enhance the flexibility and power of Next.js, enabling you to build robust and dynamic web applications with ease.

Next.js offers a comprehensive suite of features for constructing high-performance React applications. It streamlines the development process by providing server-side rendering, static site generation, API routes, and more. Utilizing these features allows you to create scalable and efficient web applications effortlessly.

Furthermore, we explored advanced Next.js functionalities like image optimization, internationalized routing, static and server-side rendering, incremental static regeneration, dynamic API routes, and deployment strategies. These features expand the potential of Next.js, solidifying its role as a powerful framework for developing high-performance, scalable web applications.

By mastering these concepts, you can effectively leverage Next.js to build robust, efficient applications that deliver outstanding user experiences.


Help us improve the content 🤩

You can leave comments here.

Last updated