When ISR might be a better option than SSR (Next JS)

Improving page load time, and user experience

I recently improved the performance, and overall user experience of a Next.JS web app I built by changing how I fetched data.

First, let's get some acronyms out of the way: SSG refers to 'Static Site Generation', SSR stands for 'Server-Side Rendering', and ISR is 'Incremental Server Rendering'.

Let's assume you're building a directory of some sort: a user can go to a search page (/search), and see a list of results. They can click each result, and be taken to the page for that item (/item/2). They can also add items to their favorites (as long as they are signed in). The description and images of these items could also be changed at anytime by the owner.

How would you go about fetching the data on the /item/[id] page?

Well, here's what I did:

  • Use NextJs' dynamic route feature in my [id].tsx file to get the requested id
  • Add a getServerSideProps function to fetch the info for that item from the back-end
  • Within the getServerSideProps function, also verify if the user is logged in and if the item is part of the user's favorites

This worked great!

Until, I started noticing a slowness in the page load, when this wasn't even an authenticated page. Granted, some other factors (like bulky packages) contributed to this as well.

Favoriting items aside, this really was a static page which was sometimes updated, and needed to show the new info without a re-build. ALSO, the Favorite button didn't need to be rendered ahead of time.

Now, here's how this was improved:

  • Use getStaticProps for data fetching instead, and add the revalidate property to the return object to trigger Incremental Static Rendering. What this does is give you all the benefits of SSG, but fetch every 'x' seconds to get updated info. Read more here
export const getStaticProps = async ({ params }) => {
     const item = await fetchFromDatabase(params.id);
     return {
        props: { item },
        revalidate: 10,
     };
}
  • Implement a useAuth hook to check if user is signed in
  • Use a loading state while verifying if the item is in the user's favorites (with client-side fetching). swr is my best bet
const FavoriteButton = () => {
    const { user } = useAuth();
    const { data: favorites, error } = useSWR();

    return <button>...</button>
}

In short, I moved the auth-related code to the client-side so it doesn't 'block' the page load. Of course, for authenticated pages, this might not always be an option.

This also gives the user a much better experience, as the item page is loaded much quicker. This is because the page is generated at build time, and rendered as static HTML, as opposed to being generated on every page load.

Win-win!