Next.js Data Fetching: Optimize Performance with the Right Method
Tags: react, nextjs
Published: 2023-06-06 03:09:47

Next.js, a popular React framework, provides powerful features for building server-rendered and statically generated web applications. One crucial aspect of web development is fetching data from APIs or databases. In this article, we will explore the different data fetching methods available in Next.js and understand when and how to use them. Whether you're a beginner or have some experience with Next.js, this guide will help you grasp the basics of data fetching in a simple and understandable manner.


### **Static Data Fetching**

Static data fetching is ideal for content that remains unchanged across all requests. It involves fetching data at build time, enabling Next.js to pre-render the pages with the data. This approach ensures fast loading times and reduces the load on your server during runtime. To implement static data fetching, you can use Next.js' **`getStaticProps`** function.

Example:

```tsx
// pages/index.tsx
import React from 'react';
import { GetStaticProps } from 'next';

interface HomeProps {
  data: string;
}

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

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

const HomePage: React.FC<HomeProps> = ({ data }) => {
  return (
    <div>
      <h1>Welcome to my website!</h1>
      <p>{data}</p>
    </div>
  );
};

export default HomePage;
```

In this example, the `getStaticProps` function fetches data from the specified API endpoint (`https://api.example.com/data`). The fetched data is then passed as a prop (`data`) to the `HomePage` component, which is pre-rendered at build time with the data.


### **Server-side Rendering (SSR)**

Server-side rendering allows you to fetch data on each request made to the server. This method is useful when you need to display dynamic content that can change frequently. When a user visits a page, the server fetches the data and generates the HTML, which is then sent to the client. Next.js provides the `getServerSideProps` function to implement server-side rendering.'

Example:

```tsx
// pages/post/[id].tsx
import React from 'react';
import { GetServerSideProps } from 'next';

interface PostProps {
  data: {
    title: string;
    body: string;
  };
}

export const getServerSideProps: GetServerSideProps<PostProps> = async (context) => {
  const { params } = context;
  const res = await fetch(`https://api.example.com/posts/${params?.id}`);
  const data = await res.json();

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

const PostPage: React.FC<PostProps> = ({ data }) => {
  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.body}</p>
    </div>
  );
};

export default PostPage;
```

In this example, the `getServerSideProps` function fetches the data for a specific post based on the `id` parameter from the API endpoint (`https://api.example.com/posts/[id]`). The fetched data is then passed as a prop (`data`) to the `PostPage` component, which is rendered on the server-side for each request.


### **Client-side Rendering (CSR)**

Client-side rendering is suitable for scenarios where data needs to be fetched after the initial page load. This method is commonly used for interactive components or when the data is not required for SEO purposes. With client-side rendering, the page is initially rendered without the data, and the data is fetched asynchronously on the client-side using JavaScript. Next.js supports client-side rendering through the `useEffect` hook or other libraries like React Query or SWR.

Example:

```tsx
// pages/products.tsx
import React, { useEffect, useState } from 'react';

const ProductsPage: React.FC = () => {
  const [products, setProducts] = useState<string[]>([]);

  useEffect(() => {
    const fetchProducts = async () => {
      const res = await fetch('https://api.example.com/products');
      const data = await res.json();
      setProducts(data);
    };

    fetchProducts();
  }, []);

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

export default ProductsPage;
```

In this example, the `useEffect` hook is used to fetch the products data from the API endpoint (`https://api.example.com/products`). The fetched data is stored in the `products` state variable, which triggers a re-render of the component. The product names are then displayed in a list on the client-side.


### **Incremental Static Regeneration (ISR)**

Incremental Static Regeneration combines the benefits of static and server-side rendering. It allows you to pre-render static pages at build time and then regenerate them periodically or on-demand. This approach is ideal for content that changes occasionally but doesn't require real-time updates. Next.js offers the `revalidate` option in `getStaticProps` to enable ISR.

Example:

```tsx
// pages/blog/[slug].tsx
import React from 'react';
import { GetStaticProps, GetStaticPaths } from 'next';

interface BlogProps {
  data: {
    title: string;
    content: string;
  };
}

export const getStaticProps: GetStaticProps<BlogProps> = async (context) => {
  const { params } = context;
  const res = await fetch(`https://api.example.com/blog/${params?.slug}`);
  const data = await res.json();

  return {
    props: {
      data,
    },
    revalidate: 60, // Re-generate page every 60 seconds
  };
};

export const getStaticPaths: GetStaticPaths = async () => {
  const res = await fetch('https://api.example.com/blog');
  const data = await res.json();

  const paths = data.map((post: { slug: string }) => ({
    params: { slug: post.slug },
  }));

  return {
    paths,
    fallback: true,
  };
};

const BlogPage: React.FC<BlogProps> = ({ data }) => {
  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.content}</p>
    </div>
  );
};

export default BlogPage;
```

In this example, the `getStaticProps` function fetches the blog post data based on the `slug` parameter from the API endpoint (`https://api.example.com/blog/[slug]`). The fetched data is then passed as a prop (`data`) to the `BlogPage` component. Additionally, the `getStaticPaths` function is used to generate dynamic paths for all blog posts. The page will be re-generated every 60 seconds to ensure the content is up to date.

**Conclusion:**
Understanding the different data fetching methods in Next.js is essential for building efficient and performant web applications. By utilizing static data fetching, server-side rendering, client-side rendering, and incremental static regeneration, you can create responsive and optimized web experiences. Experiment with these approaches and choose the one that best suits your project's needs. Happy coding!