Using SWR for Efficient Data Fetching in Next.js Applications

SWR for Efficient Data Fetching in Next.js Applications

SWR for Efficient Data Fetching in Next.js Applications

Fetching and rendering data from APIs is one of the core essentials of front-end development. The basic way of fetching data in JavaScript is to use local fetch or a third-party library like axios, input the right HTTP method for the request, including headers, parse the result in JSON, and render it to the DOM. This wouldn’t work well with modern web applications because of their architectures’ complexity. Users will only use a web app that’s fast, reliable, and responsive; that means if they request a resource, they want it delivered in <3s. As a result, developers need to use tools or libraries that improve the data-fetching experience in their applications.

In React, data fetching is a side effect, and it provides the useEffect Hook for performing this side effect. Data fetching in React typically would look like this:

However, when building server-rendered applications, Nextjs is preferred. Data fetching in Next.js can be done using different methods, depending on the desired rendering strategy, that is:

  • When you want to fetch data at build time and generate static HTML pages, Nextjs provides getStaticProps for that.
  • When you have dynamic routes and want to generate static HTML pages for each route, use getStaticPaths.
  • When you need to fetch data on each request, providing a server-rendered experience, use getServerSideProps
  • You can still use client-side data fetching when you don’t need to pre-render the data or when you want to fetch data that depends on user interactions.

It is common to see Next.js applications that make use of client-side data fetching. The challenge with this technique of data fetching is that you have to render data based on user interaction, which can lead to several issues if not handled properly.

This is why Vercel created SWR (stale-while-revalidate). Without a solution like SWR, you’re likely to face difficulties managing caching, ensuring data synchronization, handling errors, and providing real-time updates. Additionally, handling loading states can become cumbersome, and you might end up with a lot of boilerplate code just for fetching, caching, and managing the data as the codebase grows. SWR addresses these challenges by providing built-in caching, automatic cache revalidation, error retries, support for real-time updates, and a Hooks-based API that simplifies data management.

In this article, I’ll introduce you to how SWR works, its key concepts, and how to use it for efficient data fetching in client-side Next.js applications.

How SWR works

To understand how SWR works, you need to be conversant with these key concepts.

  1. Caching: Caching is like storing food in the fridge. It’s a way to save data so it can be quickly accessed later without needing to fetch it from the source (server) every time. This speeds up the process of retrieving data and reduces the load on the server.
  2. Revalidation: Revalidation is like checking if the food in the fridge is still good or needs replacing with a fresh meal. In the context of data fetching, revalidation means checking if the cached data is still valid or if it needs to be updated with new data from the server. With SWR, this process happens automatically in the background, ensuring your data is always up-to-date.
  3. Stale-While-Revalidate: Imagine you have a fridge with food inside. When you’re hungry, you grab something from the fridge (cached data). At the same time, a friend starts cooking a fresh meal (fetching new data). You eat the food from the fridge while waiting for the fresh meal to be ready. Once done, the fridge is restocked with fresh food (cache revalidation).

SWR is a data fetching library that implements the Stale-While-Revalidate (SWR) strategy. It fetches, caches, and revalidates data in the background to provide an efficient and seamless user experience.

What we’ll be building

To appreciate SWR, you need to build something with it. In this tutorial, we’ll build a product store with Nextjs. While building this demo app, you’ll get to learn the following:

  • Fetching data using the useSWR hook
  • Handling Errors and Loading States
  • Implementing optimistic UI updates with SWR
  • Implementing infinite scrolling in Next.js with SWR

Pre-requisites

You’ll need the following:

  • Nodejs ≥v16
  • Code editor (preferably VS code)
  • Code terminal
  • Package manager (yarn preferably)
  • Knowledge of JavaScript and Reactjs

The complete code is on Github, and the demo app is here.

Getting Started

Run this command on your terminal to create a nextjs project.

The product store will have a /product parent route with these sub-routes: /product/[id]and /product/upload. Run this command from the root directory on your terminal to create these page routes.

Go back to the root directory and create these UI components

Navigate back to the root director and install these packages:

See how to configure tailwindcss here.

Fetching, Displaying, and Updating Data with SWR

Firstly, let’s create a custom React hook that’ll fetch data from the Products fakeapi endpoint using SWR. Add these lines of code to hooks/useFetch.js

Here, we define a custom React hook, useFetch, which leverages the useSWR hook for data fetching. It takes a path as input and constructs a full URL to fetch data from the Fake Store API. The fetcher function handles making the request and error checking. useFetch returns an object with the fetched data, potential errors, and a loading state.

Now let’s display the product list.

Navigate to components/ProductCard and add these lines of code.

This ProductCard component renders a single product card. The card includes an image, product title, category, price, and a link to the product details page. The image is set using Next.js’ Image component, which automatically optimizes the image for performance. The link to the product details page is wrapped in Next.js’ Link component for client-side navigation.

Navigate to component/ProductList.jsx and add these lines of code.

Here, we define the ProductList component, which fetches and displays a list of products. It uses the useFetch custom hook to fetch product data and ProductCard to render each product. The component handles loading and error states and displays the products in a grid format.

Update pages/index.js with these lines of code.

Run the dev server with this command yarn dev, it should start the server at http://localhost:3000, and you should see something like this on your browser.

Product listing

Product listing

Great! We’ve been able to display the products. Let’s now implement the view for a single product. Update pages/product/[id].js with these lines of code. Here we’ll use useSWR directly.

This component handles loading and error states and renders product information. The getServerSideProps function retrieves the product ID from the query params, which is passed as a prop to the component for server-side rendering. A single product detail looks like this:

Product details

Product details

Handling Errors and Loading States

SWR provides an ErrorBoundary component that can be used to catch errors that occur within its child components. This component can be used to display a fallback UI when an error occurs, such as an error message or a reload button. The Error boundary provides a way to separate an application’s data-fetching and data-rendering concerns. When an error occurs during data fetching, SWR Error Boundary can intercept and handle it gracefully. This way, error messages can be displayed to the user without breaking the entire application.

In this code snippet, if an error occurs during the fetching process, it throws an error, which is caught by the ErrorBoundary component. The fallback prop of the ErrorBoundary component is a UI to be displayed when an error occurs. In this case, it displays a simple error message.

Implementing loading indicators for a better user experience

Let’s go back to building our app. Go to component/LoadingIndicator.jsx and add these lines of code.

This LoadingIndicator component will be used to represent the loading states while fetching data. Head to components/ProductList.jsx and modify the code to this:

Let’s also create an ErrorMessage component that renders an error message when there are data fetching issues. Head to components/ErrorMessage.jsx:

Update component/ProductList.jsx like this:

Optimistic UI updates

When you POST a message in messaging apps, it is immediately displayed in the chat even if there’s no network connection. That’s the idea of optimistic UI updates, and this can be implemented with SWR. With SWR, you can update the local cache of the data in response to user actions while at the same time sending the update request to the server. If the server responds with an error, the local cache can be reverted back to its previous state. This way, the user gets an immediate response while still having the assurance that the data is being updated on the server.

To implement optimistic UI updates with SWR, you need to use the mutate function provided by the hook. The mutate function allows you to update the cache of the data without making a request to the server. You can pass an updater function to mutate, which receives the current data as an argument and returns the updated data. The function updates the local cache with the new data and triggers a re-render of the component.

Once the update request is sent to the server, you will use mutate again to update the cache with the response from the server.

This is how to implement optimistic UI updates in our app while uploading a new product.

pages/product/upload.js

Check out the ProductUploadForm.jsx here.

Using SWR for paginated data fetching

Paginated data fetching is one of the use cases of SWR. If we’re fetching a large amount of data, breaking it down into smaller chunks called pages improves performance and reduces the amount of data transferred over the network. The useSWRInfinite hook implements pagination with SWR. It takes two arguments: getKey and fetcher.

The getKey function returns a unique key for each page based on the page index and previous page data. Returning null for an empty page prevents unnecessary requests.

The fetcher function fetches data for a given key using an HTTP client, like axios or fetch.

Once we set up the useSWRInfinite hook, we can use the data and error properties to render the data and handle errors, respectively. We can also use the isLoading property to show a loading indicator while data is being fetched.

To implement pagination, we use the size and setSize properties to control the number of pages to fetch. Incrementing the size value in a loadMore function that’s called when the user reaches the end of the current page enables pagination. We also use the hasNextPage property to determine if more data can be fetched.

Implementing infinite scrolling in Next.js with SWR

Update the useFetch hook with these lines of code:

In this code snippet is a custom hook that uses the SWR library to fetch paginated data. It takes two arguments, the path, and limit . The getKey function returns a unique key for each page based on the page index and previous page data. The hook uses the useSWRInfinite function to fetch data for a given key using an HTTP client. It returns an object with data, isLoading, isError , loadMore , and hasNextPage properties. The data property is an array of fetched data, while isLoading is a boolean value that indicates if the data is being fetched. isError is a boolean value that indicates if an error occurred while fetching data. loadMore is a function that increments the number of pages to fetch, and hasNextPage is a boolean value that indicates if there’s more data to be fetched. The fetcher function is called to fetch data from a given URL, and it throws an error if the response is not successful.

The app should work properly with infinite scrolling now. Check out the live demo here.

Wrapping up

SWR is a powerful tool for efficient data fetching in client-side Next.js applications. Its built-in caching, automatic cache revalidation, error retries, support for real-time updates, and Hooks-based API make data management simpler and more streamlined. By using SWR, developers can improve the data-fetching experience for their users and ensure their applications are fast, reliable, and responsive. With Next.js, there are different methods of data fetching, depending on the desired rendering strategy. While client-side data fetching is a popular option, it can lead to several issues if not handled properly. SWR addresses these challenges and provides a more efficient way of managing data.

Overall, SWR is a great tool for building high-performance, scalable, and reliable web applications with React and Next.js.

About the author

Stay Informed

It's important to keep up
with industry - subscribe!

Stay Informed

Looks good!
Please enter the correct name.
Please enter the correct email.
Looks good!

Related articles

26.03.2024

An Introduction to Clustering in Node.js

Picture your Node.js app starts to slow down as it gets bombarded with user requests. It's like a traffic jam for your app, and no developer likes ...

15.03.2024

JAMstack Architecture with Next.js

The Jamstack architecture, a term coined by Mathias Biilmann, the co-founder of Netlify, encompasses a set of structural practices that rely on ...

Rendering Patterns: Static and Dynamic Rendering in Nextjs

Next.js is popular for its seamless support of static site generation (SSG) and server-side rendering (SSR), which offers developers the flexibility ...

No comments yet

Sign in

Forgot password?

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy

Password recovery

You can also try to

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy