Telerik blogs
ReactT2_1200x303

Next.js provides you the flexibility to render different pages in your application using different rendering modes. In this article, we’ll cover the different strategies available to determine how best to fetch data when building your application with Next.js.

When building an application with Next.js, one of the things we need to figure out is how to fetch our data. With Next.js, there are effective ways to fetch your data while giving your user the best experience possible. Next.js is a hybrid framework, and with this flexibility, you have the freedom to use different data-fetching mechanisms for different pages.

This article will help you understand the different strategies available to you, as a developer, to determine how best to fetch data when building your application with Next.js. Basic knowledge of React and Next.js is required to understand the concepts discussed here. Let’s get started!

Client-Side Rendering

For user-specific pages, you can fetch the data on the client the same way you do when you write plain React. Since they include constantly changing data (dashboards, authentication pages) that does not need to be indexed by search engines, there’s no need to pre-render such pages. Next.js also has a React hook called SWR that optimizes your data-fetching needs on the client side. SWR handles caching, revalidation, focus tracking, re-fetching on intervals, and you can check out the documentation for more.

Pre-Rendering in Next.js

By default, every page created in the pages folder in Next.js is pre-rendered in advance. This means that the HTML for each page is generated ahead of time. After which, the JavaScript code runs to make the page fully interactive—a process known as Hydration.

Next.js version 9.3 introduced three new built-in functions for pre-rendering pages.

  • getStaticProps()
  • getStaticPaths()
  • getServerSideProps()

React requires all the necessary data before rendering the page, so getStaticProps, getStaticPaths and getServerSideProps can only be exported from a page, not from non-page files. We’ll take a look at their differences and when and how to use them in a bit.

When it comes to SEO, there are significant disadvantages to using React because pages are generated at runtime by JavaScript. Hence, nothing gets displayed until your JavaScript loads, leaving the user with a blank screen. With Next.js, the user will instantly see the page content (pre-rendered HTML). However, the page won’t be interactive (like opening a modal) until the hydration process is complete.

There are two types of pre-rendering in Next.js. The difference between them is when the HTML for a page is generated. The method you choose depends on what type of data it is and when you want it to be available to your user.

  1. Static Generation
  2. Server-side Rendering

This pre-rendering method generates the HTML for your pages on the server only at build time, meaning when you build your app for production (Next.js build). Once the HTML for your page is generated, it can be cached and served by a CDN and reused on each request, making it available to your site visitors ahead of time.

When To Use Static Generation

You should use static generation if the content of a page can be pre-rendered before a user’s request. Because the HTML for each page will be created ahead of time, avoid using static generation for data that’s only available at request time or data that frequently changes, as the data may become stale at request time. Static generation will be suitable for pages with content that rarely changes, like blog posts, data that is not user-specific, etc.

Next.js also allows you to statically generate pages with or without data. Let’s take a look at the difference between them.

Static Generation Without Data

Next.js statically pre-renders a page by default if the data is written directly in the app’s source code and doesn’t require fetching external data at build time.

    // pages/about.js
    export default function About() {
      return (
        <div>
          <h1>About Us</h1>
          <p>{/* ... */}</p>
        </div>
      );
    }

Our About Us page in the code snippet above doesn’t require fetching external data, and by default, it will be pre-rendered at build time.

Static Generation With Data and Predefined Routes

For pages that depend on external data (database, file system, etc.), all you need to do is export an async function called getStaticProps along with your page component. Next.js will know to run the getStaticProps function and fetch the data at build time. Then whatever is returned as a prop from it will be passed into the component exported from the page.

    // pages/index.js
    export default function HomePage(props) { ... }
    
    // This function will run only at build time.
    export async function getStaticProps(context) {
    // fetch data from external API 
      const res = await fetch("...");
      const data = await res.json();
    
    // Whatever is in the props object will be
    //  passed to the HomePage component
      return {
        props: {
          data
        },
      };
    }

In the code snippet above, we are fetching our data in the getStaticProps function. We’re also exporting getStaticProps in the same file as the HomePage component, and it returns an object with props. Next.js will pre-render (generate HTML) this page at build time, and the results of getStaticProps are also saved in a JSON file. The JSON file is then passed to the HomePage component as props at runtime and used for client-side page transitions (routing through next/link or next/router).

The getStaticProps function also receives the context parameter, which is an object containing some keys. One important key is the params object—it contains the route parameters for pages using dynamic routes. We’ll see how to use the params object in the next section.

Static Generation With Data and Dynamic Paths

If we want to pre-render pages with dynamic routes, first we need to define a list of possible paths to those pages so that Next.js can pre-render them at build time. To achieve this, we need to export another function called getStaticPaths along with the dynamic page component and have getStaticPaths define and return all the possible paths (ids) for the individual pages we want to be pre-rendered to HTML at build time. The getStaticPaths function defines paths for dynamic routes and tells Next.js which routes to pre-render pages for.

    // pages/posts/[id].js
    // This should be the path for each character `/posts/<id>`
    export default function PostDetailPage(props) {...}
    
    // This function will run at only at build time.
    export async function getStaticPaths() {
    
      // fetch posts from external API
      const res = await fetch("...");
      const posts = await res.json();
    
      // Generate the paths for individual pages to be
      // Pre-rendered at build time
      const paths = posts.map((post) => ({
        params: { id: post.id },
      }));
    
      // These paths will be pre-rendered at build time
      return { paths, fallback: true };
    }
    
    // This function will run only at build time.
    export async function getStaticProps({ params }) {
    
      // fetch data for each dynamic route with the post `id`
      const res = await fetch(`.../${params.id}`);
      const post = await res.json();
    
      // Whatever is in the props object will be
      //  passed to the HomePage component
      return {
        props: {
          post,
        },
      };
    }

In the example above, we want our individual post page to be static, so we’re generating the pages ahead of time (build time). Because these pages have dynamic routes, we need to fetch the data from wherever it is (database, API) and then create paths for the pages.

To achieve this, we’re using the getStaticPaths function to fetch the data and then generate paths for our dynamic URL. getStaticPaths returns an object with paths on it, and paths is an array containing the params for each dynamic URL that needs to be statically generated. In our case, params will be the id for each dynamic URL.

We’re passing the params object to the getStaticProps function, and it is going to give us access to the post id to fetch data for each page to be rendered to HTML at build time. Because it is a dynamic URL, we need to use both getStaticPaths and getStaticProps, but if it isn’t a dynamic URL, we’ll use only getStaticProps.

The getStaticPaths function also returns a boolean fallback key whose value we set to true. In the getStaticPaths function, you have the flexibility to generate the paths of some pages (important or most recent) at build time and defer the rest to be generated at request time. This means that when a user requests for a page whose path wasn’t pre-generated, at runtime it will be generated immediately for that user and pre-rendered. If we change the value of fallback to false, a 404 page will be generated instead for pages whose paths weren’t pre-generated at build time. If you have a few paths to pre-render and new pages aren’t added often, you can set the value of fallback to false.

Server-Side Rendering (SSR)

Suppose you have pages you want to pre-render, whose data needs to be updated frequently, or maybe they depend on client-side data, so you need the data at request time. You can use the getServerSideProps function.

    // pages/posts/[id].js
    export default function PostDetailPage(props) {}
    
    // Next.js will run this function on every request
    export async function getServerSideProps(context) {
    
      // fetch data from external API
      const res = await fetch("...");
      const post = await res.json();
    
      // Whatever is in the props object, post in our case
      // will be passed to the PostDetailPage component
      return {
        props: {
          post,
        },
      };
    }    

The getServerSideProps function is similar to the getStaticProps function. The difference is that, unlike getStaticProps, which generates the pages once at build time, getServerSideProps does not generate pages ahead of time. Every time a user requests a page, getServerSideProps is called. As a result, this is a good place to put dynamic data that you don’t need to generate ahead of time, such as a user’s dashboard. With server-side rendering, build times are faster because you won’t be pre-rendering pages.

Incremental Static Regeneration (ISR)

ISR is a solution that combines the power of SSG and SSR. This method allows you to create or update your pages statically on a per-page basis, eliminating the need for a complete app rebuild. With ISR, static pages are built when a user requests them and not at build time.

ISR uses the getStaticProps function to build static pages. All you need to do is add the revalidate property to your getStaticProps function’s return statement and pass a number to it. This value (in seconds) defines the amount of time after which Next.js revalidates a page.

    // pages/posts/[id].js
    export default function PostDetailPage(props) {...}
    
    //Next.js will call this function at build time
    export async function getStaticPaths() {
      const res = await fetch("...");
      const posts = await res.json();
    
      // Generate the paths for individual pages to be
      // Pre-rendered at build time
      const paths = posts.map((post) => ({
        params: { id: post.id },
      }));
    
      // These paths will be pre-rendered at build time
      return { paths, fallback: "true" };
    }
    
    // This function will run only at build time.
    export async function getStaticProps({ params }) {
    
      // fetch data for each dynamic route with the post `id`
      const res = await fetch(`.../${params.id}`);
      const post = await res.json();
    
      // Whatever is in the props object will be
      //  passed to the HomePage component
      return {
        props: {
          post,
        },
    
        // Because we enabled revalidation, this page will be regenerated
        // when a user requests the page
        revalidate: 5
      };
    }

To enable ISR, we still need to use the getStaticProps function and include the revalidate property. This property is one of the optional keys contained in the context parameter. It is an amount of time after which Next.js attempts to regenerate a page. It defaults to false on statically generated pages, which means the page will be cached as built until your next build. In the code snippet above, we’re generating the paths to a few pages at build time. When a user requests a page that was pre-rendered at build time, the user receives the initially cached page.

Because we set the value of our revalidate property to 5, any request to that same page before the five-second timeframe will also show the cached page, but Next.js will start a regeneration of that page in the background. Next.js will invalidate the cached page when the regeneration is finished and show the updated post page. However, if the revalidation is unsuccessful, the cached page will remain unchanged.

If a request is made to a path that wasn’t pre-generated at build time, Next.js will automatically generate the path for that page and cache it. The page will be available as if it were part of the first build, and subsequent requests to that page will be served from the cache. When a visitor to our site attempts to access the details of that same character, it will be provided from the cache.

The getStaticProps, getStaticPaths and getServerSideProps functions will only be executed on the server-side and never on the client-side (browser). Because it is not included in the browser’s JavaScript bundle, you can do whatever you do on the server (read the file system) inside these functions.

Conclusion

Next.js provides you the flexibility to render different pages in your application using the different rendering modes we discussed above. It all just depends on what function you export to fetch the data. Since version 9.3, Next.js has significantly improved how we fetch data while providing a fantastic developer experience, and it has several SEO-friendly features integrated into its framework.


Ifeoma-Imoh
About the Author

Ifeoma Imoh

Ifeoma Imoh is a software developer and technical writer who is in love with all things JavaScript. Find her on Twitter or YouTube.

Related Posts

Comments

Comments are disabled in preview mode.