DEV Community

Sergio Daniel Xalambrí
Sergio Daniel Xalambrí

Posted on • Updated on • Originally published at sergiodxa.com

Introduction to SWR

SWR is an all-new data fetching library made by the team at ZEIT, the same team that created Now and Next.js.

This new library is made specifically for React applications as a custom hook with a straightforward interface.

Let's build a small project using SWR and Next.js to see how it works.

Running Demo

This is the final project running in CodeSandbox

Setup Project

First, let's create the project.

npx create-next-app
Enter fullscreen mode Exit fullscreen mode

This will give us a basic Next.js application after writing the name we want for it.

Now let's install SWR and isomorphic-unfetch, this one will help us do the fetch.

yarn add swr isomorphic-unfetch
Enter fullscreen mode Exit fullscreen mode

And that's all if we run our project with yarn dev we will already have a page with some components, we could safely delete them since we are not going to use them.

The API

The API we will consume in our application is Pokeapi, this free API will give use endpoints to get information about different Pokémon.

The Initial Page

Let's start coding, first, we need to create a file pages/index.js where we will export a React component, this will be our home page.

import Head from "next/head";
import useSWR from "swr";
import fetcher from "../lib/fetcher";

function HomePage() {
  const { data } = useSWR("https://pokeapi.co/api/v2/pokemon", fetcher);

  if (!data) return <h1>Loading...</h1>;
  const { results } = data;

  return (
    <>
      <Head>
        <link
          href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"
          rel="stylesheet"
        />
      </Head>

      <section className="container mx-auto">
        <div className="-mx-2 flex flex-wrap">
          <article key={result.name}>{result.name}</article>
        </div>
      </section>
    </>
  );
}

export default HomePage;
Enter fullscreen mode Exit fullscreen mode

Note: We are using Tailwind here to have some basic styles, this is not the best way to load Tailwind in a real-world application.

Fetcher

Our fetcher above is a simple module that will run the fetch and reply with the JSON data instead of the HTTP response.

import fetch from "isomorphic-unfetch";

function fetcher(...args) {
  return fetch(...args).then(response => response.json());
}

export default fetcher;
Enter fullscreen mode Exit fullscreen mode

A Better List of Pokémon

With this we have a list of the first 20 Pokémon names, let's create a new component to show more information about each one.

import fetcher from "../lib/fetcher";
import useSWR from "swr";

function PokemonShort({ name }) {
  const { data } = useSWR(`https://pokeapi.co/api/v2/pokemon/${name}`, fetcher);

  return (
    <div className="my-5 p-2 w-1/3">
      <article className="shadow p-5 relative">
        <h2 className="font-bold text-xl capitalize">{name}</h2>
        {data ? (
          <>
            <div className="absolute top-0 right-0">
              <img src={data.sprites.front_default} />
            </div>
            <ul>
              <li>
                <strong>Weight</strong>: {data.weight}
              </li>
              <li>
                <strong>Height</strong>: {data.height}
              </li>
            </ul>
            <br />
            <h3 className="font-bold text-lg">Stats</h3>
            <ul className="flex justify-start items-baseline flex-wrap">
              {data.stats.map(stat => (
                <li key={stat.stat.name} className="w-3/6">
                  <strong className="capitalize">{stat.stat.name}</strong>:{" "}
                  {stat.base_stat}
                </li>
              ))}
            </ul>
          </>
        ) : (
          <p className="font-bold text-l capitalize">Loading {name}...</p>
        )}
      </article>
    </div>
  );
}

export default PokemonShort;
Enter fullscreen mode Exit fullscreen mode

Here we will show the Pokémon name since the beginning and only show the loading text until we have the stats, measures and sprite URL.

Now on our HomePage, we need to import and use PokemonShort.

import Head from "next/head";
import useSWR from "swr";
import fetcher from "../lib/fetcher";
import PokemonShort from "../components/pokemon-short";

function HomePage() {
  const { data } = useSWR("https://pokeapi.co/api/v2/pokemon", fetcher);

  if (!data) return <h1>Loading...</h1>;
  const { results } = data;

  return (
    <>
      <Head>
        <link
          href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"
          rel="stylesheet"
        />
      </Head>
      <section className="container mx-auto max-w-md">
        {results.map(result => (
          <PokemonShort key={result.name} name={result.name} />
        ))}
      </section>
    </>
  );
}

export default HomePage;
Enter fullscreen mode Exit fullscreen mode

Now we should see a list of cards with the data of each Pokémon.

Our Next.js + SWR Pokedex Application

With this, we saw how to use SWR to fetch a list of data and then render a component per item and fetch more data inside them.

In future articles, I will continue working on this application and add more features.

Top comments (3)

Collapse
 
tomekbuszewski profile image
Tomasz Buszewski

Hey, thanks for this article. I've heard about SWR, but haven't used it yet.

But from what I see, it's a simple hook to replace useState and useEffect. Meaning, I could go with

const [data, setData] = React.useState();
React.useEffect(() => {
  (async() => {
    const data = await fetch("...");
    setData(data.json());
  })()
}, []);

And it would have similar effect, but it would work client-side only. Or am I missing something here?

Collapse
 
sergiodxa profile image
Sergio Daniel Xalambrí

Yes, it's client-side only, it does more things internally, like revalidate when the browser tab is focused again and more feature I'm planning to write more soon.

There is a way to do SSR too, it's something I plan to write about too.

Collapse
 
tomekbuszewski profile image
Tomasz Buszewski

Oh, so it is more to it than it looks, nice.

I'll be waiting on a follow-up article then, thanks! :)