Software Development
Pawel Rybczynski, 2021-04-09

Data fetching strategies in NextJS

Recently, NextJS is gaining more and more popularity as a way to build React applications. Certainly, a major contributor is the fact that NextJS offers several different data fetching strategies.

And these are:

  • Client-side rendering: CSR,
  • Server-side rendering: SSR,
  • Static site rendering: SSG,
  • ​Incremental static regeneration: ISR.

NextJS, unlike the plain React app, offers a feature called pre-rendering. This means that pre-rendered HTML is displayed during the initial load. In traditional React applications, the entire app is loaded and rendered on the client’s side. Then, after the JS code is loaded, the application becomes interactive.

Static Generation

In SSG, the HTML is generated in build-time and reused for each request. After a production build is created, every request is going to reuse that statically generated HTML file.

There are also two types of static generation: with and without data.

code review

In the first case, the HTML will be generated after fetching the data by resolving the promise. In this case, we can use the getStaticProps data fetching method… but only if you are using Next.js 9.3 or newer. If not, think about upgrading it, because the older getInitialProps method is no longer recommended and will be deprecated. This method also is called on in every client-side navigation so it’s not efficient if you don’t want to fetch data on every request.

export async function getStaticProps() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await res.json();
  return {
    props: { posts },
  };
}

The really cool thing is a newly released feature called ISR (Incremental Static Regeneration), which is something between SSG and SSR. It allows you (by using a specific key called revalidate) to make the page incrementally regenerated. It means that with this key you don’t need to rebuild the app each time you want to get an update of the data fetched from the server. Just add the revalidate key with the revalidation period in seconds.

export async function getStaticProps() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await res.json();
  return {
    props: {
      posts,
    },
    revalidate: 30,
  };
}

It means that if requests come after that period, then it will fetch the data from the server again.

Another situation is when you use a dynamic page, and the path depends on some external data. In that case, we can use the getStaticPaths method telling which dynamic routes should be pre-loaded:

export async const getStaticProps = ({ params }) => {
  const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`);
  const post = await res.json();
  return {
    props: { post },
  };
}
export async function getStaticPaths() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await res.json();
  const paths = posts.map(({ id }) => ({ params: { id: `${id}` } }));
  return {
    paths,
    fallback: false,
  };
}

There is a nice way to check which dynamic routes have been created. You can run the next build and next export and after that you will have a static version of your app in the newly created out directory.

Let`s now limit the pre-build paths:

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

After running the next export, we can notice in both cases (with and without a limited number of paths) a different number of posts found in my-app\out\posts.

next_export

Let’s now take a closer look at the crucial and required fallback parameter. It says what to do if the page was not pre-rendered at the build-time. If it’s set to true, the getStaticProps runs and generates that page. If false, we’ll get 404 after trying to load that specific path. Another thing: pages with fallback that are enabled in getStaticPaths cannot be exported.

Below you can find the results of trying to load the same dynamic page in both cases:

First case (without limited paths)

first

Second case (with limited paths and fallback set to false)

second_fallback_false

Second case (with limited paths and fallback set to true)

second_fallback_true

Server-Side Rendering

The main difference with SSG: new HTML is generated on every request. It’s used mostly when we are fetching some external data. In this case, there is no need to rebuild the app every time we want to update the data from the server.

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

The getServerSideProps method looks very similar to getStaticProps. The difference is that getServerSideProps runs on every request, while getStaticProps runs once in build-time.

Client-Side Rendering

With client-side rendering, the initial load of the page is a bit slow. Communication with the server happens in the run-time. You can do it in a more traditional way:

const Blog = () => {


 const [posts, setPosts] = useState([]);
  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(
     "https://jsonplaceholder.typicode.com/posts");
      const posts = await res.json();
      setPosts(posts);
    };
    fetchData();
  }, []);

Also, we can use another solution, 'swr' – React Hooks library for data fetching by Vercel*, which is strongly recommended by NextJS creators. The SWR stands for State While Revalidate.

* Data source: SWR by Vercel

import useSWR from "swr";


const Blog = () => {
 const fetcher = (url) => fetch(url).then((r) => r.json());
 const { data: posts = [], error } = useSWR(
    "https://jsonplaceholder.typicode.com/posts", fetcher);
 if (error) return <div>Failed to load posts</div>;
 if (!posts.length) return <div>loading...</div>;
 return (
    <>
      {posts.map((post) => (
            {post.title}
        ))}
    </>
}

Summary

NextJS gives us the additional possibility of choosing which of the data fetching strategies we want to use on each page. We don’t need to follow only one solution for the whole application.

Shortcut

  • getServerSideProps - when you need to pre-render app on each request with fetched data
  • getStaticProps - when the data can be fetched once at build-time and used on each request with no update
  • getInitialProps - not recommended, will be deprecated
  • getStaticPaths - for pre-rendering dynamic paths

NextJs pros and cons

develop digital product

Read more:

Rails API & CORS. A dash of consciousness

Why you should (probably) use Typescript?

Highest quality code in your SaaS project. Why should you care about it as a (non-technical) founder?