12Jan
Building a Pokedex with Next.js
Building a Pokedex with Next.js

Next.js is a framework for building static and dynamic React apps. It’s production-ready and ships with handy features to get your app up and running in no-time. Next.js offers different ways of fetching data because it supports both client and server-side rendering. In this tutorial, we will be looking at Next.js; by building a Pokedex app using SWR for retrieving data. Let’s get started!

Prerequisites

This tutorial assumes a basic understanding of React and Next.js. If you’re new to Next.js, you can start with this article to get started; otherwise, let’s dive in.

What we’re building

We will be building a Pokedex app with Next.js. The pokemons will be fetched from the Pokemon API using the SWR library. This article will teach you how to build apps with Next.js. It will also introduce you to data fetching on the client-side with the SWR library.

What is Next.js?

Next.js gives you a great developer experience. It comes with several functionalities that would have been installed and handled on your own in a “vanilla” React app. Next.js. The Vercel’s team states it as follows:

Next.js gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.

The Next.js framework provides a straightforward solution to build your API using API routes. You can build your entire API with GraphQL, Express, or your favorite framework and then connect it to a database using API routes. Next.js use file-system based routing and treats files under the pages directory as routes or API endpoints.

Setting up

We will be using Create Next App to create a fresh project with a modern config and best practices. So, begin by opening your command-line interface (CLI) and running the following:

npx create-next-app next-pokedex

Once the project is created, let’s now structure it:

├── components
|  └── Pokemon.js
├── pages
|  ├── _app.js
|  └── index.js
├── package.json
├── styles.css
└── useRequest.js

Let’s break the folder structure down to make it easy to grasp:

1. components/Pokemon.js is a presentational component that receives the object to display as an argument from the parent component (index.js).
2. pages/_app.js tells Next.js how to initialize our app with a custom style. In this case, we will use styles.css to add styling to our Pokedex.
3. pages/index.js is the home page of our app. It is responsible for the fetch and the display of all pokemons when the page loads.
4. useRequest is a custom hook that relies on the SWR library to retrieve the data from the API. Creating this file remains optional; you can skip it if you want and use useSWR directly in your components. However, we will need to fetch the data in two different files, so having this hook can prevent us from repeating ourselves. With this folder structure in place, we can now get our hands dirty and code something meaningful in the next section.

Creating the custom hook

As a quick introduction, SWR is a set of React hooks for remote data fetching that makes things easier, such as caching, pagination, and so on. It’s created by the same team behind Next.js. Unlike React, the SWR library ships with Next.js, so we don’t need to install it. Let’s create our custom hook!

// useRequest.js

import useSWR from 'swr'

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

const API_URL = 'https://pokeapi.co/api/v2/pokemon'
const PAGE_LIMIT = 100

export default function useFetchPokemon(name) {
  const uri = name ? `${API_URL}/${name}` : `${API_URL}?limit=${PAGE_LIMIT}`
  const { data: result, error } = useSWR(uri, fetcher)

  return { result, error }
}

We start by importing the useSWR hook from SWR. After that, we declare the variables needed to perform the fetch request from the API. Next, we pass in the name of the pokemon to the useFetchPokemon() function. The parameter is optional, so we have to check whether the name received is valid or not, then build up the uri of the API. Finally, we fetch the data using useSWR and pass in the uri and the fetcher function as arguments. By the way, the fetcher function allows us to transform the data retrieved to a JSON format.

Creating the Pokemon component

// Pokemon.js

import React from 'react'
import useFetchPokemon from '../useRequest'

export default function Pokemon({ pokemon }) {
  const { name } = pokemon
  const { result, error } = useFetchPokemon(name)

  if (error) return <h1>Something went wrong!</h1>
  if (!result) return <h1>Loading...</h1>

  return (
    <div className='Card'>
      <span className='Card--id'>#{result.id}</span>
      <img
        className='Card--image'
        src={result.sprites.front_default}
        alt={name}
      />
      <h1 className='Card--name'>{name}</h1>
      <span className='Card--details'>
        {result.types.map((poke) => poke.type.name).join(', ')}
      </span>
    </div>
  )
}

As you can see here, we first import the custom hook created earlier. This component will receive an object that contains the name of the pokemon from the index.js file. With the pokemon’s name, we can now use useFetchPokemon() to retrieve the data and then display it accordingly. With this step further, we can update the index.js file and use the Pokemon component to show the data once the page loads.

Fetching all pokemons from the API

// index.js

import useFetchPokemon from '../useRequest'
import Pokemon from '../components/Pokemon'

export default function IndexPage() {
  const { result, error } = useFetchPokemon()

  if (error) return <h1>Something went wrong!</h1>
  if (!result) return <h1>Loading...</h1>

  return (
    <main className='App'>
      <h1>My pokemons</h1>
      <div>
        {result.results.map((pokemon) => (
          <Pokemon pokemon={pokemon} key={pokemon.name} />
        ))}
      </div>
    </main>
  )
}

In this file, we start by importing the custom hook and the component responsible for displaying the pokemons. Next, we pull out from useFetchPokemon() the result object that holds the data fetched from the API, and an error state for the case something went wrong. After that, we loop through the response data, and for each element, we rely on the Pokemon component to display it. With this step, our Pokedex app is almost done. We just need to add some styling to make it looks good.

Styling the Pokedex app

Here is the complete CSS file:

@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400;700&display=swap');

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background: #00303f;
  line-height: 1.5;
  font-family: 'Caveat', cursive;
}

.App {
  text-align: center;
  margin: 2rem 0;
  padding: 1rem;
  max-width: 1100px;
  margin: auto;
}

.App > h1 {
  text-align: center;
  color: #ffffff;
  margin-bottom: 2rem;
  font-size: 2rem;
  text-transform: uppercase;
}

.App > div {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  grid-gap: 1.5rem;
  justify-content: center;
  align-items: center;
}

.Card {
  width: 12rem auto;
  background: #5bb0ca;
  color: #e4c439;
  padding: 1rem;
  border-radius: 10px;
  border-top: 0.5px solid #e4c439;
  border-bottom: 0.5px solid #e4c439;
  text-align: center;
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
  position: relative;
  overflow: hidden;
  cursor: pointer;
  transition: 0.2s ease-in-out all;
}

.Card:hover {
  transform: scale(1.05);
}

.Card--id {
  background: #e4c439;
  width: 3rem;
  color: #fff;
  padding: 0.1rem;
  font-weight: 700;
  position: absolute;
  border-radius: 0 0 10px 0;
  top: 0;
  left: 0;
}
.Card--name {
  text-transform: capitalize;
  color: rgb(255, 255, 255);
  font-size: 2rem;
  font-weight: 700;
}

.Card--image {
  width: 150px;
  display: block;
  margin: auto;
}

.Card--details {
  font-size: 1.3rem;
  color: #e4c439;
}

Next.js is a bit different to React when it comes to styling. Next.js uses the App component to initialize pages. To override the default behavior of the component, we need to use the _app.js file. If you have global styles or data that need to be shared across your components, put them here. And don’t forget to add the underscore symbol (_); otherwise, Next.js will treat the file as a normal page.

import '../styles.css'

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

By importing the styles.css file, Next.js will be able to use the CSS file to style our Pokedex app. With this, the app is ready to be tested on the browser. So, open the project directory in the CLI and then run this command:

yarn dev

## or

npm run dev

If everything works as expected, you should be able to see the Next.js app here: http://localhost:3000/.

My pokemons in Pokedex
My pokemons in Pokedex

And that’s it! Our Next.js app is looking good!

We’ve built a Pokedex App with Next.js, SWR, and the Pokemon API. You can preview the finished project in this CodeSandbox. You can find other great content like this on my blog or follow me on Twitter to get notified.

Conclusion

In this tutorial, we learned how to use Next.js and SWR to build a Pokedex app from scratch. Next.js is an amazing and popular framework built on top of React, Node.js, Babel, and Webpack. Next.js supports both client and server-side rendering, which makes Next great regarding SEO. Because only the needed JavaScript will be loaded to the client. Next.js is widely used by big companies and definitely worth the hype. You should give it a try on your next project! Thanks for reading!

Resources

Check out these resources to dive deeper into the content of this tutorial:

Getting started with Next.js
Next.js basics
Data fetching in Next.js — How To Use SWR
Learn Next.js
Next.js Starter Templates

Building React Components Using Children Props and Context API

React provides a number of powerful patterns to compose components; for example, Containment, Specialization, and Render Props. Today we’ll dive into the Containment pattern which, on the surface, looks like an easy-to-understand interface — but the example provided in React docs doesn’t have an explicit explanation of how to pass data from the parent container to its children.

Leave a Reply