How to Fetch Data in React with useSWR
Tags: webdev, react, beginners, nextjs
Published: 2023-01-04 11:14:52

**Author: <a  target="_blank" href="https://refine.dev/blog/author/michael/">Michael Hungbo</a>**


## Introduction
Data is unquestionably an important component of any modern web application today. And, as the web evolves and innovates, new methods of interacting with this data must emerge in order to provide users with a better experience when interacting with our applications. 

There have been a variety of technologies used for client-side data fetching. JavaScript, for example, has the native `Fetch() API` for data fetching. Axios is a promised-based HTTP client library for making asynchronous HTTP requests and fetching any data on the client-side. 

SWR is one of the most powerful client-side data fetching libraries for frontend frameworks today. In this article, we will explore the features and benefits of using SWR in an example Next.js application, and provide a step-by-step guide for getting started with the library.

Whether you are new to React.js or an experienced developer looking to optimize your data fetching strategy, SWR is a powerful tool worth considering! Now, let's get started!

Steps we'll cover:

- [What is SWR and useSWR?](#what-is-swr-and-useswr)
- [Setting up an Example App With SWR](#setting-up-an-example-app-with-swr)
- [Additional Features of SWR](#additional-features-of-swr)

## Prerequisites

To follow along well with this tutorial, it's essential that you have basic knowledge of JavaScript and React. SWR is a library for use with React and its frameworks, so it's required that you're familiar with the basics of both of these technologies.

In addition, make sure you have [Node](https://nodejs.org/) installed on your computer before continuing. 


## What is SWR and `useSWR`?

SWR is an acronym for stale-while-revalidate. It's a lightweight React.js library with hooks for data fetching on the client-side. SWR is bootstrapped with various hooks that are used for various performance improvement techniques such as data caching, re-validation, pagination, and many others.

The latest version of SWR, SWR 2.0, was released on December 9 and it features some new and exciting updates such as data preloading, new mutation APIs, improved optimistic UI capabilities, and a brand new DevTools for better developer experience. 

`useSWR`, on the other hand is the most basic hook from the SWR library for data fetching. A basic example using the hook is shown below:

```javascript
const { data, error} = useSWR(key, fetcher)
```

The `useSWR` hook accepts two arguments. One, a `key` value (usually the API endpoint to fetch data from), and a fetcher, which is an async function that contains the logic for fetching the data.

From the above example, `useSWR` returns two values: `data`, which is the value returned from the resource you're fetching, and a `error`, which contains error, if any is caught. It also returns two additional values, `isLoading` and `isValidating`, depending on the state of the fetcher function.

In the next section, we'll see how we can use this powerful hook as well as the other capabilities of SWR in an example application.

## Setting up an Example App With SWR

We'll create an example application in React to get started with understanding how SWR works and how to start using it. Basically, we'll compare and contrast fetching data with the native 'Fetch() API' (or other client-side data fetching libraries such as Axios) you're used to and fetching data with SWR.

You can check out the complete code for the application on [GitHub](https://github.com/Mich45/swr-tutorial).

Run the following command in your terminal to create a new React project with `create-react-app`:

```bash
npx create-react-app my-swr
```
or with Yarn:

```bash
yarn create react-app my-swr
```
**NOTE**: I named the app `my-swr` for brevity but you can also choose any name you're okay with. 

After the installation is complete, `cd` into the project directory and start the development server with `npm start` or `yarn start`. 

Next, run the following command to install the necessary packages for our example application.

```bash
npm install styled-components swr
```
or with Yarn:

```bash
yarn add styled-components swr
```

### Creating a Server

For the example application we'll be building, we'll set up a server with express running on `http://localhost:3001`. The server basically returns a list of users that will be rendered on the client-side. 

In the project root folder, create a new `server` folder, navigate to it on the command line and run the following command to create a new Node.js project with an auto-generated `package.json` file:

```bash
npm init -y
```
or with Yarn:

```bash
yarn init -y
```
Next, we'll install the necessary packages to build up our server.

```bash
npm install express cors nodemon
```
with Yarn:

```bash
yarn add express cors nodemon
```

Still in the server folder, create a file named `index.js` with the following code:

```javascript
// server/index.js

const express = require('express');
const cors = require('cors')
const app = express();

app.use(cors());
const data = [
    {
        "name": "Kim Doe",
        "age": 23,
        "avatar": "https://randomuser.me/api/portraits/women/44.jpg"
    },
    {
        "name": "Mary Jane",
        "age": 25,
        "avatar": "https://randomuser.me/api/portraits/women/50.jpg"
    },
    {
        "name": "Ken Joe",
        "age": 24,
        "avatar": "https://randomuser.me/api/portraits/women/18.jpg"
    }
    ];

app.get('/', (req, res) => {
    res.json(data);
});

app.listen(3001, () => {
    console.log('App listening on port 3001');
});

```

In the above code, we're basically creating a server in express.js and also, we created a dummy data array for the list of users we'll render on the client-side. We also added `cors` to allow our client application communicate with the server without running into errors.

Next, we'll edit the content of `server/package.json` to the following:

```javascript
// server/package.json

{
  "name": "server",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.18.2"
  },
  "scripts": {
    "dev": "nodemon index.js"
  }
}
```


NOTE: I have `nodemon` installed globally so it's not included in the list of dependencies, but you'll find it included in yours. Also, if you're using git you may want to create a `.gitignore` file in the server folder to avoid pushing the files in `node_modules`.

We can now run `npm dev` or `yarn dev` (depending on the package manager you used) to start up the server. If everything works correctly you should see `App listening on port 3001` logged to the console.

### Data Fetching with `Fetch() API` 

Let's go back to the client-side code now that we have the server up and running. In this part, we'll fetch the users from our backend (the server we created earlier) and display them in a simple component.

Navigate to the `src` folder in the root directory and create a folder named `components`. In the `components` folder create a `Home.js` file with the following code:

```javascript 

// src/components/Home.js

import React from 'react'
import { useState, useEffect } from 'react'

const Home = () => {
  const [users, setUsers] = useState(null);
    useEffect(() => {
      async function fetchUsers() {
        const response = await fetch('http://localhost:3001');
        const data = await response.json();
        setUsers(data)
      }
      fetchUsers();
    }, [])
  
    if (!users) return <h2>Loading...</h2>
    return (
      <>
        <div>
          {
            users.map((user, index) => {
             return <h2 key={index}>{user.name}</h2>
            })
          }
        </div>
      </>
    )
}

export default Home
```

Next, edit the content of `src/App.js` to the following:

```javascript 
// src/App.js

import './App.css';
import Home from './components/Home'

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Home/>
      </header>
    </div>
  );
}

export default App;
```
In the code above, we're importing the `Home.js` component we created to render it in the homepage of our application. Now, if you navigate to `http://localhost:3000`, the list of users from the server should be displayed as shown below:



<div class="img-container">
    <div class="window">
        <div class="control red"></div>
        <div class="control orange"></div>
        <div class="control green"></div>
    </div>
   <img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-01-03-use-swr/Home.png"  alt="useswr" />

</div>

<br />

Our app works correctly. Awesome! Now, let's see how the application behaves when we add a new user to the user list on the server.

In the `server/index.js` file, add a new user to the `data` array like below:

```javascript

// server/index.js

...
const data = [
    ...
    //highlight-start
    {
        "name": "John Doe",
        "age": 24,
        "avatar": "https://randomuser.me/api/portraits/women/18.jpg"
    }
    //highlight-end
]

```
Notice how the list isn't updated with the new user when we navigate back to `http://localhost:3000`. It's only when we reload the page before we get the updated list of users. 

The GIF below shows this even better:



<div class="img-container">
    <div class="window">
        <div class="control red"></div>
        <div class="control orange"></div>
        <div class="control green"></div>
    </div>
   <img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-01-03-use-swr/fetch.gif"  alt="useswr" />

</div>

<br />

Inconsistencies in UI updates and data fetching behavior could be disastrous for applications that rely on real-time data to provide a reactive and seamless experience to users. This is one major problem that SWR aims to solve.

### Data Fetching With SWR
In this part, we'll see how SWR outshines other data fetching methods by automatically re-validating and updating data without requiring us to manually refresh or reload the application.

Edit the content of `src/components/Home.js` to the following:

```javascript
// src/components/Home.js

import React from 'react'
import useSWR from 'swr'

const Home = () => {
  const fetcher = (...args) => fetch(...args).then(res => res.json());
  const { data, error, isLoading } = useSWR('http://localhost:3001', fetcher)
  
    if (error) return <div>Failed to fetch users.</div>
    if (isLoading) return <h2>Loading...</h2>
    return (
      <>
        <div>
          {
            data.map((user, index) => {
             return <h2 key={index}>{user.name}</h2>
            })
          }
        </div>
      </>
    )
}

export default Home
```

Let's go through the code above to understand what's going on.

From the code above, we import the `useSWR` hook from SWR. The hooks take a key, that is, the URL of our server and a fetcher function that contains the logic for fetching the data. The hook then returns three values based on the state of the result from the server. 

The `data` object contains the list of users and the `error` will contain any error that is thrown. Next, we map through the users array that is returned in the `data` object and render them on the page.

Now, if you navigate to `http://localhost:3000`, the list of users should be displayed like below:



<div class="img-container">
    <div class="window">
        <div class="control red"></div>
        <div class="control orange"></div>
        <div class="control green"></div>
    </div>
   <img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-01-03-use-swr/Home-2.png"  alt="useswr" />

</div>

<br />

So far so good. Now, let's also add a new user to the list on the server and see how the application behaves. Open the `server/index.js` file and add a new user to the data array like so:

```javascript

// server/index.js

...
const data = [
    ...
     //highlight-start
    {
        "name": "Peter Pan",
        "age": 24,
        "avatar": "https://randomuser.me/api/portraits/women/21.jpg"
    }
    //highlight-end
]

```
Go to `http://localhost:3000` in the browser and note how the users list is automatically updated with the new user we added without us reloading the page. 

The GIF below shows this in action.



<div class="img-container">
    <div class="window">
        <div class="control red"></div>
        <div class="control orange"></div>
        <div class="control green"></div>
    </div>
   <img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-01-03-use-swr/swr.gif"  alt="useswr" />

</div>

<br />

Notice how fast and consistent the UI is updated with the new user!

This example shows one of the incredible powers of SWR over other client-side data fetching methods. It shows how SWR is able to auto re-validate and update the UI instantly using cached data and returning the latest user from the server.

How SWR is able to infer that the data has been updated and then re-validating and updating the UI is beyond the scope of this article. However, if you're curious to know, you can read more about it [here](https://swr.vercel.app/docs/revalidation) in the docs.


---


## Is your CRUD app overloaded with technical debt?


Meet the headless, React-based solution to build sleek **CRUD** applications. With refine, you can be confident that your codebase will always stay clean and boilerplate-free.

Try [refine](https://s.refine.dev/devto) to rapidly build your next **CRUD** project, whether it's an admin panel, dashboard, internal tool or storefront.

    

<div>
<a href="https://github.com/refinedev/refine">
   <img  src="https://refine.dev/img/generic_banner.png" alt="refine blog logo" /> 
</a>
</div>

---

## Additional Features of SWR
We saw one of SWR's best features in action in the previous section; this section will show you even more of what SWR is made and capable of.

### Pagination
SWR simplifies paginating data using the `useSWR` hook. We'll see a basic example of paginating data with `useSWR` using the [Rick and Morty Character API](https://rickandmortyapi.com/documentation/#introduction).

In the components folder of our example application, create a file named `Characters.js` and add the following code to it:

```javascript

// src/components/Characters.js

import React from "react";
import { useState } from "react";
import useSWR from "swr";
import styled from "styled-components";

const Wrapper = styled.div`
  margin: 50px auto;
`;
const Character = styled.div`
  width: 250px;
  height: 200px;
  border-radius: 6px;
  outline: none;
  border: none;
`;

const Button = styled.button`
  width: 120px;
  height: 40px;
  border-radius: 6px;
  background-color: #6c6ce3;
  margin-inline: 10px;
  outline: none;
  border: none;
  color: white;
  font-weight: bold;
`;

const Container = styled.div`
width: 100%;
margin: 0 auto;
display: grid;
gap: 10px;
padding-block: 20px;
grid-template-columns: 2fr 2fr 2fr 2fr;
`;


const Characters = () => {
  const [pageIndex, setPageIndex] = useState(1);

  const fetcher = (...args) => fetch(...args).then((res) => res.json());

  const { data, error, isLoading } = useSWR(`https://rickandmortyapi.com/api/character/?page=${pageIndex}`,fetcher);

  if (error) return <div>Failed to fetch characters.</div>;
  if (isLoading) return <h2>Loading...</h2>;

  return (
    <>
      <Container>
        {data.results.map((character) => (
            <Character key={character.id}>
                <img width={100} height={100} src={character.image} />
                <div>{character.name}</div>
            </Character>
        ))}
      </Container>
        <Wrapper>
          <Button onClick={() => setPageIndex(pageIndex - 1)}>Previous</Button>
          <Button onClick={() => setPageIndex(pageIndex + 1)}>Next</Button>
        </Wrapper>
    </>
  );
};

export default Characters;

```

In the above code, we are fetching characters from the Rick and Morty API. The endpoint returns 20 characters per request. We also used a React state to keep track of the pages so we can simply move forward and backward through the fetched characters to create a pagination effect.

Next, open the `App.js` file in the root folder and edit its content to this:

```javascript
// App.js

import './App.css';
// import Home from './components/Home'
import Characters from './components/Characters'

function App() {
  return (
    <div className="App">
      <header className="App-header">
        {/* <Home/> */}
        <Characters/>
      </header>
    </div>
  );
}

export default App;
```
Basically, what we're doing is commenting out the previous Home component and then import and render the `Characters` component on the homepage instead.

Navigate to `http://localhost:3000` and you should see the characters rendered as shown in the GIF below, with pagination enabled:



<div class="img-container">
    <div class="window">
        <div class="control red"></div>
        <div class="control orange"></div>
        <div class="control green"></div>
    </div>
   <img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-01-03-use-swr/pagination.gif"  alt="useswr" />

</div>

<br />


You can read more about pagination in SWR in the [docs](https://swr.vercel.app/docs/pagination).

### Preloading Data With SWR

Another notable use case of SWR is data preloading. With SWR, we can prefetch data, for example, fetching blog posts from a CMS or prefetching page routes in Next.js for faster and smooth client-side transitions.

We can prefetch data using the `preload` API from SWR. The following example from the docs shows how you can preload a user component on the click of a button:

```javascript
import { useState } from 'react'
import useSWR, { preload } from 'swr'

const fetcher = (url) => fetch(url).then((res) => res.json())

// Preload the resource before rendering the User component below,
// this prevents potential waterfalls in your application.
// You can also start preloading when hovering the button or link, too.
preload('/api/user', fetcher)

function User() {
  const { data } = useSWR('/api/user', fetcher)
  ...
}

export default function App() {
  const [show, setShow] = useState(false)
  return (
    <div>
      <button onClick={() => setShow(true)}>Show User</button>
      {show ? <User /> : null}
    </div>
  )
}
```

<br/>
<div>
<a href="https://discord.gg/refine">
  <img  src="https://refine.ams3.cdn.digitaloceanspaces.com/website/static/img/discord_big_blue.png" alt="discord banner" />
</a>
</div>




### SWRDevTools 
The newest version of SWR comes with developer tools for debugging and writing your code more efficiently and confidently. You can install the SWRDevTools extensions for both Chrome and Firefox from their respective stores.

* Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
* Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/

Be aware that SWRDevTools is not an official project of Vercel. You can learn more about the project from their [website](https://swr-devtools.vercel.app/).

### TypeScript Support

SWR also comes with full TypeScript support out of the box. If you're using TypeScript you can be rest assured of type safety in your  application. For example, SWR will infer the argument types of fetcher from key, so you can have the preferred types automatically. 

You can also explicitly specify the types for `key` and `fetcher`'s arguments like so:

```javascript
import useSWR, { Key, Fetcher } from 'swr'

const uid: Key = '<user_id>'
const fetcher: Fetcher<User, string> = (id) => getUserById(id)

const { data } = useSWR(uid, fetcher)
// `data` will be `User | undefined`.

```
You can read more about using TypeScript with SWR [here](https://swr.vercel.app/docs/typescript).

This section only touched on a few of SWR's many capabilities, so your first encounter with it will not be overwhelming. You can however, check the [documentation](https://swr.vercel.app/docs/getting-started) for other features that might interest you.



## Conclusion

In this article, we learned about the basics of SWR, its importance in developing modern web applications, and how you can get started with it. We also created an example application in React and explored some capabilities of SWR to see its use cases and benefits over other conventional client-side data fetching methods.  

I hope you found this article useful and that you will begin using SWR to achieve the performance improvements you've always desired in your applications!