Creating GIFs with Giphy API and ReactJS: A Beginner's Guide.
Tags: javascript, beginners, react, tutorial
Published: 2023-06-06 13:37:14

# Introduction
Do you find yourself continually browsing Giphy in search of the ideal GIF to convey your thoughts? Ever thought you could make your own original GIFs to express your distinct personality and sense of style? You don't need to look any farther because the Giphy API and ReactJS make it simple to realize your creative concept.

In this beginner's guide, I will walk you through the simple steps to creating your very own GIFs using Giphy API and ReactJS. Whether you're a seasoned developer or a coding newbie, I've got you covered. All you need is a little bit of creativity and a desire to learn.

This is an illustration of how our project will be structured:

{% embed https://www.loom.com/share/a96e6b656f6c449b8e46e553330b4960 %}


First, we'll start with the basics;

## What is Giphy API?

Giphy API is a database of GIFs that allows developers to access and display GIFs on their web applications. With Giphy API, you can easily integrate GIFs into your projects, making your web applications more engaging and fun.

## Getting Started

Before we begin building our web application, you must first set up a basic ReactJS development environment. I'll be using create-react-app as the initial project template for this tutorial.

In order to use the Giphy API, you must first register a [Giphy account.](https://developers.giphy.com/) Don't worry, it's a simple and free procedure. Simply go to the Giphy website and click on the **Create button** in the upper right corner of the screen. After you've completed the basic steps to create your account, you'll be able to use the Giphy API and begin developing your web application.

![giphy documentation dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcmdpp1o4uutobfhicrj.png)

Once you've created your account on Giphy, you'll be directed to the [Giphy dashboard](https://developers.giphy.com/dashboard/). To create an app, simply click on the **Create an App button** within the dashboard. You can choose a suitable name for your app, such as **"React Project"**. During the app creation process, you'll be presented with the option to use either the **Giphy SDK** or the **Giphy API**. For the purpose of this tutorial, we'll be selecting the GIPHY API option.

After successfully creating your app, you will be given an API key. This API key is required to use the Giphy API. Keep your API key private and secure.

You can now access the Giphy API endpoint using your API key. The API_URL variable in the provided code sample represents the endpoint URL for fetching trending GIFs. In the URL, replace "abcd1234" with your actual API key.

![fetching data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tsrreyl2u9a61zvzs76.png)

By the end of this tutorial, you will not only have a functional web application that shows random GIFs, but you will also have the knowledge and skills to develop your own GIFs and integrate them into your projects. So, let's get started and bring out your inner GIF master!

## How to Set Up a React Project
Type the following command in your terminal:

```javascript
npx create-react-app giphy-api
```
This will create a new React project named **giphy-api** in the current directory.

In this project, we will utilize various tools and libraries to enhance our development process. We'll leverage Axios for making API calls, React Icons for adding stylish icons, React Infinite Scroll Component for seamless scrolling functionality, React Router DOM for handling routing, and SCSS as our CSS library for flexible styling options.

We would install the following dependencies using the Node Package Manager (NPM) in your command prompt or terminal. Here are the steps:

Open your command prompt or terminal,
Navigate to your project directory using the 'cd' command.
Type the following command and press enter:

```javascript
cd giphy-api
```

```javascript
npm install axios react-icons react-infinite-scroll-component react-router-dom
```

Start the development server by running the following command:

```javascript
npm start
```
After running the command, the application will be launched and can be accessed at **localhost:3000**. This is the default local development server URL where the application is hosted.

Remove the default App.test.js, logo.svg, reportWebVitals.js, setUpTests.js and index.css you have already created.

Notice, you'll come across an error on your local server:

![react error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/klqbdp8pqyyiitq8ynna.png)

A simple way to handle this error is basically to navigate to the index.js file and remove the index.css and reportWebVitals(). You will notice another compilation error at App.js because the compiler is trying to import logo.svg which we no longer have.

## Setting Up The App File and Folder Structure

For this tutorial, we'll follow the standard file and folder structure for ReactJS projects. Start by navigating to the **src** directory and create two separate new folder named components and also **pages**. Inside the components folder, create subfolders called **NavBar**, **SearchBar**, and **Spinner**.

In addition, you will need to create a `.env` file in the project's root directory where you will store your API key. Whatever name you give your variable, you must add REACT_APP in front of it, as in the following example:

![react folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iacx67didqfosdxy67lr.png)

```javascript
REACT_APP_API_KEY = your-api-key-goes-here
```

In your code, you can access this environment variable using the process.env object. For example, if your API key is stored in an environment variable called REACT_APP_API_KEY, you can access it like this:

```javascript
const API_KEY = process.env.REACT_APP_API_KEY;
```

Navigate to the **src** directory and create a subdirectory called **commons**. Inside the "commons" directory, create two files: **Icons.js** and **Input.js**.

Inside the **Icons.js** file add the following code;

```javascript
const UserIcons = ({ icons: Icons, className, id, onClick = () => {} }) => {
  return (
    <>{Icons && <Icons className={className} id={id} onClick={onClick} />}</>
  );
};
export default UserIcons;
```

Navigate to **Input.js** and add;

```javascript
const Input = ({
  type,
  className,
  name,
  placeholder,
  onChange = () => {},
  onBlur = () => {},
  onFocus = () => {},
  value,
}) => {
  return (
    <>
      <input
        type={type}
        className={className}
        name={name}
        placeholder={placeholder}
        required
        onChange={onChange}
        onBlur={onBlur}
        onFocus={onFocus}
        value={value}
      />
    </>
  );
};
export default Input;
```

## Exploring The Giphy API: Making API Calls

To prepare for displaying GIFs data, let's navigate to the **pages** directory. Inside the **pages** directory, create a sub-directory called **Giphy**. Within the Giphy directory, create a component named **GifGallery.js**. Before rendering the GIFs, we'll start by making an API request.

Navigate to the root component **App.js** and add the following code;

```javascript
import React, { useEffect, useState } from "react";
import axios from "axios";
import GifGallery from "./pages/Giphy/GifGallery";
import "./App.css";

// API key and URL
const API_KEY = process.env.REACT_APP_API_KEY;
const API_URL = "https://api.giphy.com/v1/gifs/";

const App = () => {
  // State variables
  const [giphyData, setGiphyData] = useState([]);

  // Fetch data on mount
  useEffect(() => {
    // Function to fetch GIFs data
    const fetchGifs = async () => {
      try {
        let endpoint = "";

        // Construct the API endpoint with required parameters
        endpoint = `${API_URL}trending?api_key=${API_KEY}&limit=25&offset=0&rating=Y&lang=en`;

        // Send API request to fetch GIFs data
        const results = await axios.get(endpoint);

        // Set the retrieved GIFs data to the state variable
        setGiphyData(results.data.data);
      } catch (error) {
        console.error(error);
      }
    };

    // Call the fetchGifs function when the component mounts
    fetchGifs();
  }, []);

  return (
    <div className="giphy-container">
      {/* Render the GifGallery component and pass the GIFs data as props */}
      <GifGallery giphyData={giphyData} />
    </div>
  );
};

export default App;
```

Navigate to the **App.css** file and add the following styles:

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

body {
  background-color: #121212;
  font-family: interface, "Helvetica Neue", helvetica, sans-serif;
}

body::-webkit-scrollbar {
  display: none;
}

.giphy-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  width: 847px;
  margin: 0 auto;
  overflow: hidden;
}

.error-container {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

.error-icon {
  font-size: 32px;
  color: red;
}

.error-message {
  color: rgb(255, 255, 255);
  font-size: 18px;
  margin-left: 8px;
}

/* Media Query */

@media only screen and (min-width: 614px) and (max-width: 844px) {
  body {
    width: 100%;
  }

  body::-webkit-scrollbar {
    display: none;
  }

  .giphy-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    width: 100%;
    margin: 0 auto;
    overflow: hidden;
  }
}

@media only screen and (min-width: 321px) and (max-width: 613px) {
  body {
    width: 100%;
  }

  body::-webkit-scrollbar {
    display: none;
  }

  .giphy-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    max-width: 321px;
    margin: 0 auto;
    overflow: hidden;
  }
}
```

Navigate to **GifGallery.js** and add the following code;

```javascript
import React from "react";
import PropTypes from "prop-types";
import "./GifGallery.css";

// GifGallery component displays a gallery of GIF images
const GifGallery = ({ giphyData }) => {
  return (
    <div className="gif">
      {giphyData.map((gif, index) => (
        <div className="gif__image-container" key={index}>
          <img
            className="gif__image"
            src={gif.images.fixed_height.url}
            alt="gif"
          />
        </div>
      ))}
    </div>
  );
};

// Define the prop types for GifGallery component
GifGallery.propTypes = {
  giphyData: PropTypes.array.isRequired, // giphyData prop should be an array and is required
};

export default GifGallery;
```

Navigate to the **GifGallery.css** file and add the following styles:

```javascript
.gif {
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgb(255, 255, 255);
  border-radius: 0px;
  outline: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  position: relative;
  width: 870px;
  flex-wrap: wrap;
  overflow: hidden;
}
.gif__image-container {
  margin: 8px;
  cursor: pointer;
  color: rgb(255, 255, 255);
  position: relative;
  vertical-align: baseline;
  position: relative;
}
.gif__image {
  color: rgb(255, 255, 255);
  border-radius: 8px;
  height: 200px;
  width: 200px;
}

@media only screen and (min-width: 614px) and (max-width: 844px) {
  .gif {
    display: flex;
    align-items: center;
    justify-content: center;
    font: inherit;
    position: relative;
    width: 100%;
    max-width: 614px;
    flex-wrap: wrap;
    overflow: hidden;
  }
  .gif__image-container {
    margin: 8px;
    cursor: pointer;
    color: rgb(255, 255, 255);
    position: relative;
    vertical-align: baseline;
    position: relative;
  }
  .gif__image {
    color: rgb(255, 255, 255);
    border-radius: 8px;
    height: 188px;
    width: 188px;
  }
}
@media only screen and (min-width: 321px) and (max-width: 613px) {
  .gif {
    display: flex;
    align-items: center;
    justify-content: center;
    font: inherit;
    position: relative;
    width: 100%;
    max-width: 321px;
    flex-wrap: wrap;
    overflow: hidden;
  }
  .gif__image-container {
    margin: 8px;
    cursor: pointer;
    color: rgb(255, 255, 255);
    position: relative;
    vertical-align: baseline;
    position: relative;
  }
  .gif__image {
    color: rgb(255, 255, 255);
    border-radius: 8px;
    height: 144px;
    width: 144px;
  }
}
```

## Searching for Gifs

First, we need to navigate to the components directory located inside the src directory. Once there, we can create a NavBar directory. Inside the NavBar directory, we should create two files: **NavBar.js**, which will contain the code for the NavBar component, and **NavBar.css**, which will hold the styles for the NavBar.

Inside the **NavBar.js** add the following lines of code:

```javascript
import UserIcons from "commons/Icons";
import { FaEllipsisV } from "react-icons/fa";
import Button from "commons/Button";
import SearchBar from "components/SearchBar/SearchBar";
import GiphyLogo from "assets/gif/giphy-logo.gif";
import Svgs from "assets/svgs";
import { Link, useLocation, useNavigate } from "react-router-dom";
import "./NavBar.css";

const NavBar = ({ searchTerm, setSearchTerm, handleSubmit, handleClick }) => {
  const menuItems = [
    { title: "Reactions", path: "/" },
    { title: "Entertainment", path: "/" },
    { title: "Sports", path: "/" },
    { title: "Stories", path: "/" },
    { title: "Artists", path: "/" },
    {
      title: <UserIcons icons={FaEllipsisV} />,
      className: "navigation__menu-ellipsis-container",
    },
  ];

  // React Router hooks
  let navigate = useNavigate();
  let location = useLocation();

  // Handle navigation to the home page
  const handleHomeNavigation = () => {
    setSearchTerm("");
    if (location.pathname !== "/") {
      navigate("/");
    }
  };

  return (
    <header className="navigation">
      <nav className="navigation__content">
        {/* Giphy logo */}
        <div
          className="navigation__logo-container"
          onClick={handleHomeNavigation}
        >
          <Svgs.GiphyLogo className="navigation__giphy-logo" />
        </div>

        {/* Small Giphy logo */}
        <img
          className="navigation__gif-small-logo"
          src={GiphyLogo}
          alt="giphy-logo.gif"
          onClick={handleHomeNavigation}
        />

        {/* Menu items */}
        <div className="navigation__menu-container">
          <ul className="navigation__menu-list">
            {menuItems.map((item, index) => (
              <li
                className={
                  item === "UserIcons"
                    ? "navigation__menu-ellipsis-container"
                    : "navigation__menu-list-item"
                }
                key={index}
              >
                <Link
                  className="navigation__menu-link"
                  to={item.path}
                  onClick={item.path ? handleHomeNavigation : null}
                >
                  {item.title}
                </Link>
              </li>
            ))}
          </ul>
        </div>

        {/* Buttons */}
        <div className="navigation__gif--sticker-btn-container">
          <span className="navigation__stickers-container">
            <Button className="navigation__stickers-btn" title="stickers" />
          </span>

          <span
            className="navigation__stickers-container"
            onClick={() => {
              navigate("/");
            }}
          >
            <Button className="navigation__stickers-btn" title="GIFs" />
          </span>
        </div>
      </nav>

      {/* SearchBar component */}
      <SearchBar
        searchTerm={searchTerm}
        setSearchTerm={setSearchTerm}
        handleSubmit={handleSubmit}
        handleClick={handleClick}
      />

      {/* Explore section */}
      <div className="navigation__explore-container">
        <p className="navigation__explore-content">
          Explore <span className="navigation__explore-gif-text">explore</span>{" "}
          GIFs
        </p>
      </div>
    </header>
  );
};

export default NavBar;
```

Navigate to **NavBar.css** and add the following code;

```javascript
.navigation {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  color: rgb(255, 255, 255);
  letter-spacing: 0px;
  text-align: left;
  margin: 0px;
  border: 0px;
  padding: 10px 0 0 0;
  justify-content: center;
  width: 100%;
  position: relative;
  z-index: 499;
  overflow: hidden;
}
.navigation__content {
  display: flex;
  color: rgb(255, 255, 255);
  letter-spacing: 0px;
  text-align: left;
  list-style: none;
  border-radius: 0px;
  outline: none;
  text-decoration: none;
  -webkit-font-smoothing: antialiased;
  margin: 0px;
  border: 0px;
  font: inherit;
  width: 100%;
  height: 70px;
  padding: 10px 0px 0px;
  position: relative;
  z-index: 499;
}
.navigation__logo-container {
  border-radius: 0px;
  outline: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  color: rgb(255, 255, 255);
  display: flex;
  align-items: center;
  margin-top: 12px;
  transform: translate3d(0px, 0px, 0px);
  transition: transform 0.3s cubic-bezier(0.165, 0.84, 0.44, 1) 0s;
  cursor: pointer;
}
.navigation__giphy-logo {
  letter-spacing: 0px;
  color: rgb(255, 255, 255);
  border: 0px;
  outline: none;
  width: 164px;
}
.navigation__gif-small-logo {
  display: none;
}
.navigation__menu-container {
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgb(255, 255, 255);
  letter-spacing: 0px;
  text-align: left;
  list-style: none;
  outline: none;
  text-decoration: none;
  padding: 0px;
  border: 0px;
  font: inherit;
  margin: 10px 0px 0px 14px;
  width: 58.559%;
}
.navigation__menu-list {
  color: rgb(255, 255, 255);
  letter-spacing: 0px;
  text-align: left;
  outline: none;
  text-decoration: none;
  padding: 0px;
  border: 0px;
  font-size: 15px;
  vertical-align: baseline;
  list-style: none;
  margin: 0px;
  display: flex;
  align-items: center;
  justify-content: space-around;
  transform: translateZ(0px);
  background-position-x: 219px;
  width: 100%;
  cursor: pointer;
}
.navigation__menu-list::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 4px;
  width: 99.2%;
  background: linear-gradient(
      to right,
      rgb(0, 204, 255),
      rgb(153, 51, 255) 31%,
      rgb(230, 70, 182) 52%,
      rgb(255, 249, 170) 77%,
      rgb(0, 255, 153),
      rgb(0, 204, 255)
    )
    0% 50%/200% 50%;
}
.navigation__menu-list-item {
  color: rgb(255, 255, 255);
  letter-spacing: 0px;
  list-style: none;
  border-radius: 0px;
  outline: none;
  text-decoration: none;
  -webkit-font-smoothing: antialiased;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  cursor: pointer;
  height: 36px;
  border-right: 4px solid rgb(18, 18, 18);
  position: relative;
  display: flex;
  align-items: center;
  flex-grow: 1;
  align-items: center;
  text-transform: capitalize;
}
.navigation__menu-list-item:hover {
  background: #3167b7;
}
.navigation__menu-link {
  letter-spacing: 0px;
  list-style: none;
  text-transform: capitalize;
  border-radius: 0px;
  outline: none;
  text-decoration: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  vertical-align: baseline;
  width: 100%;
  text-align: center;
  font-weight: 600;
  color: rgb(255, 255, 255);
  height: 100%;
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
}
.navigation__menu-ellipsis-container {
  letter-spacing: 0px;
  list-style: none;
  cursor: pointer;
  text-transform: capitalize;
  border-radius: 0px;
  outline: none;
  text-decoration: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  vertical-align: baseline;
  font-family: interface;
  text-align: center;
  font-size: 15px;
  font-weight: 600;
  color: rgb(255, 255, 255);
  height: 100%;
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
}
.navigation__gif--sticker-btn-container {
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgb(255, 255, 255);
  letter-spacing: 0px;
  text-align: left;
  list-style: none;
  border-radius: 0px;
  outline: none;
  text-decoration: none;
  -webkit-font-smoothing: antialiased;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  vertical-align: baseline;
  display: flex;
  margin-top: 10px;
  margin-left: 14px;
}
.navigation__stickers-container {
  letter-spacing: 0px;
  text-align: left;
  list-style: none;
  outline: none;
  text-decoration: none;
  -webkit-font-smoothing: antialiased;
  margin: 0px;
  border: 0px;
  font: inherit;
  vertical-align: baseline;
  color: rgb(255, 255, 255);
  padding: 8.5px 16px;
  display: inline-block;
  border-radius: 2px;
  background-size: 400% !important;
  background: linear-gradient(
    263.31deg,
    rgb(153, 51, 255) -97.49%,
    rgb(97, 87, 255) 94.14%
  );
  margin-right: 4px;
  background-position-x: -108px;
}
.navigation__stickers-btn {
  letter-spacing: 0px;
  text-align: left;
  list-style: none;
  border-radius: 0px;
  outline: none;
  text-decoration: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  color: rgb(255, 255, 255);
  font-size: 15px;
  font-weight: 600;
  background: 0 0;
  text-transform: capitalize;
  cursor: pointer;
}
.navigation__explore-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: rgb(255, 255, 255);
  text-align: left;
  text-decoration: none;
  padding: 0px;
  font-size: inherit;
  margin: 10px 0 15px 0;
  position: relative;
  width: 100%;
}
.navigation__explore-content {
  text-align: left;
  font: inherit;
  color: #fff;
  font-size: 36px;
  font-weight: 800;
  display: block;
}
.navigation__explore-gif-text {
  text-align: left;
  margin: 0px;
  padding: 0px;
  font: inherit;
  vertical-align: baseline;
  color: #00e6cc;
}

@media only screen and (min-width: 614px) and (max-width: 844px) {
  .navigation {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    padding: 10px 0 0 0;
    justify-content: center;
    width: 100%;
    max-width: 614px;
    position: relative;
    z-index: 499;
    overflow: hidden;
  }
  .navigation__content {
    display: flex;
    align-items: center;
    justify-content: space-between;
    color: rgb(255, 255, 255);
    width: 100%;
  }
  .navigation__logo-container {
    color: rgb(255, 255, 255);
    display: flex;
    align-items: center;
    margin-top: 12px;
    transform: translate3d(0px, 0px, 0px);
    transition: transform 0.3s cubic-bezier(0.165, 0.84, 0.44, 1) 0s;
    cursor: pointer;
  }
  .navigation__giphy-logo {
    color: rgb(255, 255, 255);
    border: 0px;
    outline: none;
    width: 164px;
  }
  .navigation__gif-small-logo {
    display: none;
  }
  .navigation__menu-container {
    display: none;
  }
  .navigation__gif--sticker-btn-container {
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgb(255, 255, 255);
    margin: 0px;
    padding: 0px;
    border: 0px;
    font-size: 15px;
    margin-top: 10px;
    margin-left: 14px;
  }
  .navigation__stickers-container {
    margin: 0px;
    border: 0px;
    font-size: 15px;
    color: rgb(255, 255, 255);
    padding: 8.5px 16px;
    display: inline-block;
    border-radius: 2px;
    background-size: 400% !important;
    background: linear-gradient(
      263.31deg,
      rgb(153, 51, 255) -97.49%,
      rgb(97, 87, 255) 94.14%
    );
    margin-right: 4px;
    background-position-x: -108px;
  }
  .navigation__stickers-btn {
    font-size: 15px;
    color: rgb(255, 255, 255);
    font-weight: 600;
    background: 0 0;
    text-transform: capitalize;
    cursor: pointer;
  }
  .navigation__explore-container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    color: rgb(255, 255, 255);
    text-align: left;
    text-decoration: none;
    padding: 0px;
    font-size: inherit;
    margin: 10px 0 15px 0;
    position: relative;
    width: 100%;
  }
  .navigation__explore-content {
    display: block;
    font-size: 32px;
  }
  .navigation__explore-gif-text {
    text-align: left;
    margin: 0px;
    padding: 0px;
    font: inherit;
    vertical-align: baseline;
    color: #00e6cc;
  }
}
@media only screen and (min-width: 321px) and (max-width: 613px) {
  .navigation {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    color: rgb(255, 255, 255);
    padding: 10px 0 0 0;
    justify-content: center;
    width: 100%;
    max-width: 321px;
    position: relative;
    z-index: 499;
    overflow: hidden;
  }
  .navigation__content {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
  }
  .navigation__logo-container {
    display: none;
  }
  .navigation__gif-small-logo {
    display: flex;
    border: 0px;
    outline: none;
    width: 140px;
    cursor: pointer;
  }
  .navigation__menu-container {
    display: none;
  }
  .navigation__gif--sticker-btn-container {
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgb(255, 255, 255);
    margin: 0px;
    padding: 0px;
    border: 0px;
    font-size: 13px;
    margin-top: 10px;
    margin-left: 14px;
  }
  .navigation__stickers-container {
    margin: 0px;
    border: 0px;
    font-size: 13px;
    color: rgb(255, 255, 255);
    padding: 6.5px 14px;
    display: inline-block;
    border-radius: 2px;
    background-size: 400% !important;
    background: linear-gradient(
      263.31deg,
      rgb(153, 51, 255) -97.49%,
      rgb(97, 87, 255) 94.14%
    );
    margin-right: 4px;
    background-position-x: -108px;
  }
  .navigation__stickers-btn {
    font-size: 13px;
    color: rgb(255, 255, 255);
    font-weight: 600;
    background: 0 0;
    text-transform: capitalize;
    cursor: pointer;
  }
  .navigation__explore-container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    color: rgb(255, 255, 255);
    text-align: left;
    text-decoration: none;
    padding: 0px;
    font-size: inherit;
    margin: 10px 0 15px 0;
    position: relative;
    width: 100%;
  }
  .navigation__explore-content {
    display: block;
    font-size: 28px;
  }
  .navigation__explore-gif-text {
    text-align: left;
    margin: 0px;
    padding: 0px;
    font: inherit;
    vertical-align: baseline;
    color: #00e6cc;
  }
}
```

Create a sub-directory called **SearchBar** inside the components directory. Within the SearchBar directory, generate two files: **SearchBar.js** and **SearchBar.css**.

Navigate to the **SearchBar.js** file and the following code;

```javascript
import React, { useEffect, useState } from "react";
import Input from "commons/Input";
import "./SearchBar.css";

const SearchBar = ({
  searchTerm,
  setSearchTerm,
  handleSubmit,
  handleClick,
}) => {
  const [currentPlaceholder, setCurrentPlaceholder] = useState(0);
  const placeholders = [
    "Search for GIFs",
    "Find your favorite GIFs",
    "Discover trending GIFs",
  ];

  useEffect(() => {
    const interval = setInterval(() => {
      setCurrentPlaceholder((prev) => (prev + 1) % placeholders.length);
    }, 2000);

    return () => clearInterval(interval);
  }, [placeholders.length]);

  return (
    <div className="search">
      <form className="search__form" onSubmit={handleSubmit}>
        <Input
          id="search"
          className="search__input"
          type="text"
          value={searchTerm}
          name="search-input"
          onChange={(event) => setSearchTerm(event.target.value)}
          placeholder={placeholders[currentPlaceholder]}
        />

        <button className="search__button-container" onClick={handleClick}>
          <img
          className='search__search-icon'
            src="https://giphy.com/static/img/search-icon.svg"
            width="30"
            alt=""
          />
        </button>
      </form>
    </div>
  );
};

export default SearchBar;
```

Navigate to **SearchBar.css** and add the following;

```javascript
.search {
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgb(255, 255, 255);
  border-radius: 0px;
  outline: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  position: relative;
  margin-left: auto;
  margin-right: auto;
  transition: width 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
  width: 100%;
  height: 45px;
  margin: 12px 0px 0px 0px;
}
.search__form {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 52px;
  position: relative;
}
.search__input {
  font-family: inherit;
  line-height: normal;
  color: rgb(0, 0, 0);
  outline: none;
  padding: 0px 17px;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border-radius: 0px;
  font-weight: normal;
  width: 100%;
  border: 0px;
  margin: 0px;
  z-index: 2;
  position: relative;
  height: 52px;
  letter-spacing: 1px;
  font-size: 18px;
  border-radius: 4px;
  background-color: #ffffff;
}
.search__input::-moz-placeholder {
  text-align: left;
  list-style: none;
  color: rgb(166, 166, 166);
  letter-spacing: 1px;
  -moz-user-select: none;
  user-select: none;
  border-radius: 0px;
  outline: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  transform: translateY(0px);
  animation: 5s ease 0s infinite normal none running gVMIwq;
}
.search__input::placeholder {
  text-align: left;
  list-style: none;
  color: rgb(166, 166, 166);
  letter-spacing: 1px;
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  border-radius: 0px;
  outline: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  transform: translateY(0px);
  animation: 5s ease 0s infinite normal none running gVMIwq;
}
.search__button-container {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: left;
  border-radius: 0px;
  outline: none;
  margin: 0px;
  padding: 0px;
  border: 0px;
  font: inherit;
  color: rgb(255, 255, 255);
  width: 52px;
  height: 52px;
  animation: 2s linear 0s infinite normal none running CNual;
  background-image: linear-gradient(
    45deg,
    rgb(153, 51, 255) 0%,
    rgb(255, 102, 102) 50%,
    rgb(153, 51, 255) 100%
  );
  background-size: 400%;
  background-position: 0% 100%;
  inset: 0px;
  border-radius: 4px;
  position: absolute;
  left: 795px;
  z-index: 1000;
  cursor: pointer;
}

@media only screen and (min-width: 614px) and (max-width: 844px) {
  .search {
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    margin-left: auto;
    margin-right: auto;
    transition: width 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
    width: 100%;
    max-width: 614px;
    height: 45px;
    margin: 12px 0px 0px 0px;
  }
  .search__form {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 52px;
    position: relative;
  }
  .search__input {
    font-family: inherit;
    line-height: normal;
    color: rgb(0, 0, 0);
    outline: none;
    padding: 0px 17px;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    border-radius: 0px;
    font-weight: normal;
    width: 100%;
    border: 0px;
    margin: 0px;
    z-index: 2;
    position: relative;
    height: 52px;
    letter-spacing: 1px;
    font-size: 18px;
    border-radius: 4px;
    background-color: #ffffff;
  }
  .search__input::-moz-placeholder {
    text-align: left;
    list-style: none;
    color: rgb(166, 166, 166);
    letter-spacing: 1px;
    -moz-user-select: none;
    user-select: none;
    border-radius: 0px;
    outline: none;
    margin: 0px;
    padding: 0px;
    border: 0px;
    font: inherit;
    transform: translateY(0px);
    animation: 5s ease 0s infinite normal none running gVMIwq;
  }
  .search__input::placeholder {
    text-align: left;
    list-style: none;
    color: rgb(166, 166, 166);
    letter-spacing: 1px;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    border-radius: 0px;
    outline: none;
    margin: 0px;
    padding: 0px;
    border: 0px;
    font: inherit;
    transform: translateY(0px);
    animation: 5s ease 0s infinite normal none running gVMIwq;
  }
  .search__button-container {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: left;
    border-radius: 0px;
    outline: none;
    margin: 0px;
    padding: 0px;
    border: 0px;
    font: inherit;
    color: rgb(255, 255, 255);
    width: 52px;
    height: 52px;
    animation: 2s linear 0s infinite normal none running CNual;
    background-image: linear-gradient(
      45deg,
      rgb(153, 51, 255) 0%,
      rgb(255, 102, 102) 50%,
      rgb(153, 51, 255) 100%
    );
    background-size: 400%;
    background-position: 0% 100%;
    inset: 0px;
    border-radius: 4px;
    position: absolute;
    left: 562px;
    z-index: 1000;
    cursor: pointer;
  }
}
@media only screen and (min-width: 321px) and (max-width: 613px) {
  .search {
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    margin-left: auto;
    margin-right: auto;
    transition: width 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
    width: 100%;
    max-width: 321px;
    height: 45px;
    margin: 12px 0px 0px 0px;
  }
  .search__form {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 45px;
    position: relative;
  }
  .search__input {
    font-family: inherit;
    line-height: normal;
    color: rgb(0, 0, 0);
    outline: none;
    padding: 0px 17px;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    border-radius: 0px;
    font-weight: normal;
    width: 100%;
    border: 0px;
    margin: 0px;
    z-index: 2;
    position: relative;
    height: 45px;
    letter-spacing: 1px;
    font-size: 16px;
    border-radius: 4px;
    background-color: white;
  }
  .search__input::-moz-placeholder {
    text-align: left;
    list-style: none;
    color: rgb(166, 166, 166);
    letter-spacing: 1px;
    -moz-user-select: none;
    user-select: none;
    border-radius: 0px;
    outline: none;
    margin: 0px;
    padding: 0px;
    border: 0px;
    font: inherit;
    transform: translateY(0px);
    animation: 5s ease 0s infinite normal none running gVMIwq;
  }
  .search__input::placeholder {
    text-align: left;
    list-style: none;
    color: rgb(166, 166, 166);
    letter-spacing: 1px;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    border-radius: 0px;
    outline: none;
    margin: 0px;
    padding: 0px;
    border: 0px;
    font: inherit;
    transform: translateY(0px);
    animation: 5s ease 0s infinite normal none running gVMIwq;
  }
  .search__button-container {
    display: flex;
    align-items: center;
    justify-content: center;
    font: inherit;
    color: rgb(255, 255, 255);
    width: 45px;
    height: 45px;
    animation: 2s linear 0s infinite normal none running CNual;
    background-image: linear-gradient(
      45deg,
      rgb(153, 51, 255) 0%,
      rgb(255, 102, 102) 50%,
      rgb(153, 51, 255) 100%
    );
    background-size: 400%;
    background-position: 0% 100%;
    inset: 0px;
    border-radius: 4px;
    position: absolute;
    left: 276px;
    z-index: 1000;
    cursor: pointer;
  }
  .search__search-icon {
    width: 25px;
  }
}
```

Update **App.js** with the following code;

```javascript
import React, { useEffect, useState } from "react";
import axios from "axios";
import GifGallery from "pages/Giphy/GifGallery";
import NavBar from "components/NavBar/NavBar";
import "./App.css";

// API key and URL
const API_KEY = process.env.REACT_APP_API_KEY;
const API_URL = "https://api.giphy.com/v1/gifs/";

const App = () => {
  // State variables
  const [giphyData, setGiphyData] = useState([]);
  const [searchTerm, setSearchTerm] = useState("");

  // Fetch data on mount
  useEffect(() => {
    const fetchGifs = async () => {
      try {
        let endpoint = "";

        endpoint = `${API_URL}trending?api_key=${API_KEY}&limit=25&offset=0&rating=Y&lang=en`;

        const results = await axios.get(endpoint);
        setGiphyData(results.data.data);
      } catch (error) {
        console.error(error);
      }
    };

    fetchGifs();
  }, [searchTerm]);

  // Handle search input and submit
  const handleSearch = async () => {
    try {
      const endpoint = `${API_URL}search?api_key=${API_KEY}&q=${searchTerm}&_limit=25&rating=Y&lang=en`;

      const results = await axios.get(endpoint);

      setGiphyData(results.data.data);
    } catch (error) {
      console.error(error);
    }
  };

  const handleSubmit = async (event) => {
    event.preventDefault();
    handleSearch();
  };

  const handleClick = async () => {
    handleSearch();
  };

  return (
    <div className="giphy-container">
      {/* Render the NavBar component and pass necessary props */}
      <NavBar
        searchTerm={searchTerm}
        setSearchTerm={setSearchTerm}
        handleSubmit={handleSubmit}
        handleClick={handleClick}
        API_KEY={API_KEY}
      />

      {/* Render the GifGallery component and pass the GIFs data as props */}
      <GifGallery giphyData={giphyData} />
    </div>
  );
};

export default App;
```

![giphy image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c56stgglb92hjtezcre8.png)


## Implement a Loading Function and Error Handling

It is necessary to implement a loading function. This function will display a spinner at the top of the screen while retrieving the data. Once the data is successfully obtained, the spinner will disappear. To accomplish this, a two-state variable will be utilized. One state will indicate when the application is actively loading, while the other will signify when the loading process is complete.

To proceed, navigate to the components directory and create a subdirectory named "Spinner". Inside this subdirectory, create two files: **Spinner.js** and **Spinner.css**.

Inside **Spinner.js** add the following code;

```javascript
/* eslint-disable jsx-a11y/heading-has-content */
import React from "react";
import "./Spinner.css";

const Spinner = () => {
  return (
    <div className="loader">
      <div className="circle yellow"></div>
      <div className="circle red"></div>
      <div className="circle blue"></div>
      <div className="circle violet"></div>
    </div>
  );
};

export default Spinner;
```

Inside the **Spinner.css** add this;

```javascript
.loader {
  display: flex;
  align-items: center;
  justify-content: center;
}
.loader .circle {
  width: 3vw;
  height: 3vw;
  border-radius: 100%;
  margin: 2vw;
  background-image: linear-gradient(
    145deg,
    rgba(255, 255, 255, 0.5) 0%,
    rgba(0, 0, 0, 0) 100%
  );
  animation: bounce 1.5s 0.5s linear infinite;
}
.loader .yellow {
  background-color: yellow;
}
.loader .red {
  background-color: red;
  animation-delay: 0.1s;
}
.loader .blue {
  background-color: blue;
  animation-delay: 0.2s;
}
.loader .violet {
  background-color: violet;
  animation-delay: 0.3s;
}
@keyframes bounce {
  0%,
  50%,
  100% {
    transform: scale(1);
    filter: blur(0px);
  }
  25% {
    transform: scale(0.6);
    filter: blur(3px);
  }
  75% {
    filter: blur(3px);
    transform: scale(1.4);
  }
}
```

In case of any errors, such as failure to retrieve data or inability to connect to the API server, it is important to provide the user with an appropriate error message or notification.

Update the **App.js** file with the following code;

```javascript
import React, { useEffect, useState } from "react";
import axios from "axios";
import GifGallery from "pages/Giphy/GifGallery";
import NavBar from "components/NavBar/NavBar";
import Spinner from "components/Spinner/Spinner";
import UserIcons from "commons/Icons";
import { FaRegWindowClose } from "react-icons/fa";
import "./App.css";

// API key and URL
const API_KEY = process.env.REACT_APP_API_KEY;
const API_URL = "https://api.giphy.com/v1/gifs/";

// App Component
const App = () => {
  // State variables
  const [giphyData, setGiphyData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const [searchTerm, setSearchTerm] = useState("");

  // Fetch data on mount
  useEffect(() => {
    const fetchGifs = async () => {
      setIsError(false);

      try {
        let endpoint = "";

        // Fetch trending gifs
        endpoint = `${API_URL}trending?api_key=${API_KEY}&limit=25&offset=0&rating=Y&lang=en`;

        const results = await axios.get(endpoint);
        setGiphyData(results.data.data);
      } catch (error) {
        setIsError(true);
        console.error(error);
      }
    };

    fetchGifs();
  }, [searchTerm]);

  // Handle search input and submit
  const handleSearch = async () => {
    setIsError(false);
    setIsLoading(true);

    try {
      // Fetch gifs based on search term
      const endpoint = `${API_URL}search?api_key=${API_KEY}&q=${searchTerm}&_limit=25&rating=Y&lang=en`;

      const results = await axios.get(endpoint);

      setGiphyData(results.data.data);
    } catch (error) {
      console.error(error);
      setIsError("Oops! Something went wrong. Please try again later.");
    }

    setIsLoading(false);
  };

  const handleSubmit = async (event) => {
    event.preventDefault();
    handleSearch();
  };

  const handleClick = async () => {
    handleSearch();
  };

  return (
    <div className="giphy-container">
      {/* Render the NavBar component and pass necessary props */}
      <NavBar
        searchTerm={searchTerm}
        setSearchTerm={setSearchTerm}
        handleSubmit={handleSubmit}
        handleClick={handleClick}
        API_KEY={API_KEY}
        setIsError={setIsError}
        setIsLoading={setIsLoading}
      />

      {/* Conditional rendering based on isLoading and isError */}
      {isLoading ? (
        <Spinner />
      ) : isError ? (
        <div className="error-container">
          <UserIcons className="error-icon" icons={FaRegWindowClose} />
          <p className="error-message">
            Oops! Something went wrong. Please try again later.
          </p>
        </div>
      ) : (
        <GifGallery giphyData={giphyData} />
      )}
    </div>
  );
};

export default App;
```

## Infinite Scroll: Implementing a Load More Function for Seamless Content Loading

By adding a Load More function, users can seamlessly retrieve and display additional content without interruptions.

Update the **App.js** file with this code;

```javascript
import React, { useEffect, useState } from "react";
import axios from "axios";
import GifGallery from "pages/Giphy/GifGallery";
import NavBar from "components/NavBar/NavBar";
import Spinner from "components/Spinner/Spinner";
import UserIcons from "commons/Icons";
import { FaRegWindowClose } from "react-icons/fa";
import "./App.css";

// API key and URL
const API_KEY = process.env.REACT_APP_API_KEY;
const API_URL = "https://api.giphy.com/v1/gifs/";

// App Component
const App = () => {
  // State variables
  const [giphyData, setGiphyData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const [searchTerm, setSearchTerm] = useState("");
  const [currentPage, setCurrentPage] = useState(1);

  // Fetch data on mount
  useEffect(() => {
    const fetchGifs = async () => {
      setIsError(false);

      try {
        let endpoint = "";

    // Fetch trending gifs
        endpoint = `${API_URL}trending?api_key=${API_KEY}&limit=25&offset=0&rating=Y&lang=en`;

        const results = await axios.get(endpoint);
        setGiphyData(results.data.data);
      } catch (error) {
        setIsError(true);
        console.error(error);
      }
    };

    fetchGifs();
  }, [searchTerm]);

  // Handle search input and submit
  const handleSearch = async () => {
    setIsError(false);
    setIsLoading(true);

    try {
 // Fetch gifs based on search term
      const endpoint = `${API_URL}search?api_key=${API_KEY}&q=${searchTerm}&_limit=25&rating=Y&lang=en`;

      const results = await axios.get(endpoint);

      setGiphyData(results.data.data);
    } catch (error) {
      console.error(error);
      setIsError("Oops! Something went wrong. Please try again later.");
    }

    setIsLoading(false);
  };

  const handleSubmit = async (event) => {
    event.preventDefault();
    handleSearch();
  };

  const handleClick = async () => {
    handleSearch();
  };

  // Function to load more data from Giphy API
  const loadMoreData = async () => {
    try {
      let endpoint = "";

      // Determine the API endpoint based on whether a search term is present or not
      if (searchTerm) {
        endpoint = `${API_URL}search?q=${searchTerm}&api_key=${API_KEY}&limit=25&offset=${
          currentPage * 25
        }&rating=Y&lang=en`;
      } else {
        endpoint = `${API_URL}trending?api_key=${API_KEY}&limit=25&offset=${
          currentPage * 25
        }&rating=Y&lang=en`;
      }

      // Make a GET request to the API endpoint
      const results = await axios.get(endpoint);
      const newData = results.data.data;

      // Update the state with the new data
      setGiphyData((prevData) => [...prevData, ...newData]);
      setCurrentPage((prevPage) => prevPage + 1);
    } catch (error) {
      // Handle errors by resetting the data and logging the error
      setGiphyData([]);
      console.log(error);
    } finally {
      // Reset the loading and error states
      setIsLoading(false);
      setIsError(false);
    }
  };

  return (
    <div className="giphy-container">
      {/* Render the NavBar component and pass necessary props */}
      <NavBar
        searchTerm={searchTerm}
        setSearchTerm={setSearchTerm}
        handleSubmit={handleSubmit}
        handleClick={handleClick}
        API_KEY={API_KEY}
        setIsError={setIsError}
        setIsLoading={setIsLoading}
      />

      {/* Conditional rendering based on isLoading and isError */}
      {isLoading ? (
        <Spinner />
      ) : isError ? (
        <div className="error-container">
          <UserIcons className="error-icon" icons={FaRegWindowClose} />
          <p className="error-message">
            Oops! Something went wrong. Please try again later.
          </p>
        </div>
      ) : (
        <GifGallery giphyData={giphyData} loadMoreData={loadMoreData} />
      )}
    </div>
  );
};

export default App;
```

Update **GifGallery.js** with the following code;

```javascript
import React from "react";
import PropTypes from "prop-types";
import InfiniteScroll from "react-infinite-scroll-component";
import Spinner from "components/Spinner/Spinner";
import "./GifGallery.css";

// GifGallery component that renders a gallery of GIF images with infinite scroll
const GifGallery = ({ giphyData, loadMoreData }) => {
  return (
    // InfiniteScroll component from react-infinite-scroll-component library
    <InfiniteScroll
      dataLength={giphyData.length} // Current length of giphyData array
      next={loadMoreData} // Function to load more data
      hasMore={true} // Indicates whether there is more data to load
      loader={<Spinner />} // Component to display while loading more data
    >
      <div className="gif">
        {/* Render each GIF image in the giphyData array */}
        {giphyData.map((gif, index) => (
          <div
            className="gif__image-container"
            key={index}
            onClick={() => window.open(gif.images.preview_gif.url)}
          >
            <img
              className="gif__image"
              src={gif.images.fixed_height.url}
              alt="gif"
            />
          </div>
        ))}
      </div>
    </InfiniteScroll>
  );
};

// PropTypes for type checking the props
GifGallery.propTypes = {
  giphyData: PropTypes.array.isRequired, // Array of GIF data
  loadMoreData: PropTypes.func.isRequired, // Function to load more data
};

export default GifGallery;
```

## Fetching Stickers

Prior to implementing the sticker fetching function, it is essential to incorporate routes for seamless navigation between GIFs and stickers, eliminating the need for page refresh.

An error page will be created to handle situations when a user accesses a route that does not exist. Navigate to the components directory and locate the pages directory, inside the pages directory, create a file named **ErrorPage.js**.

Add the following code to **ErrorPage.js**;

```javascript
import React from "react";

const ErrorPage = () => {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        marginTop: "24px",
        fontSize: "24px",
        color: "white",
        fontWeight: "bold",
      }}
    >
      404 Error: Page Not Found.
    </div>
  );
};

export default ErrorPage;
```

Go to the components directory and create a file named **GifRoute** we are going keep the  empty  for now with just our standard dummy React snippets.

Go to back to the root file **App.js** and update the codes;

```javascript
import React, { useEffect, useState } from "react";
import axios from "axios";
import NavBar from "components/NavBar/NavBar";
import Spinner from "components/Spinner/Spinner";
import UserIcons from "commons/Icons";
import { FaRegWindowClose } from "react-icons/fa";
import GifRoute from "components/GifRoute";
import "./App.css";

// API key and URL
const API_KEY = process.env.REACT_APP_API_KEY;
const API_URL = "https://api.giphy.com/v1/gifs/";

// App Component
const App = () => {
  // State variables
  const [giphyData, setGiphyData] = useState([]);
  const [stickerData, setStickerData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const [searchTerm, setSearchTerm] = useState("");
  const [currentPage, setCurrentPage] = useState(1);

  // Fetch data on mount
  useEffect(() => {
    const fetchGifs = async () => {
      setIsError(false);

      try {
        let endpoint = "";

        // Fetch trending gifs
        endpoint = `${API_URL}trending?api_key=${API_KEY}&limit=25&offset=0&rating=Y&lang=en`;

        const results = await axios.get(endpoint);
        setGiphyData(results.data.data);
      } catch (error) {
        setIsError(true);
        console.error(error);
      }
    };

    fetchGifs();
  }, [searchTerm]);

  // Handle search input and submit
  const handleSearch = async () => {
    setIsError(false);
    setIsLoading(true);

    try {
      // Fetch gifs based on search term
      const endpoint = `${API_URL}search?api_key=${API_KEY}&q=${searchTerm}&_limit=25&rating=Y&lang=en`;

      const results = await axios.get(endpoint);

      setGiphyData(results.data.data);
    } catch (error) {
      console.error(error);
      setIsError("Oops! Something went wrong. Please try again later.");
    }

    setIsLoading(false);
  };

  const handleSubmit = async (event) => {
    event.preventDefault();
    handleSearch();
  };

  const handleClick = async () => {
    handleSearch();
  };

  // Function to load more data from Giphy API
  const loadMoreData = async () => {
    try {
      let endpoint = "";

      // Determine the API endpoint based on whether a search term is present or not
      if (searchTerm) {
        endpoint = `${API_URL}search?q=${searchTerm}&api_key=${API_KEY}&limit=25&offset=${
          currentPage * 25
        }&rating=Y&lang=en`;
      } else {
        endpoint = `${API_URL}trending?api_key=${API_KEY}&limit=25&offset=${
          currentPage * 25
        }&rating=Y&lang=en`;
      }

      // Make a GET request to the API endpoint
      const results = await axios.get(endpoint);
      const newData = results.data.data;

      // Update the state with the new data
      setGiphyData((prevData) => [...prevData, ...newData]);
      setCurrentPage((prevPage) => prevPage + 1);
    } catch (error) {
      // Handle errors by resetting the data and logging the error
      setGiphyData([]);
      console.log(error);
    } finally {
      // Reset the loading and error states
      setIsLoading(false);
      setIsError(false);
    }
  };

  return (
    <div className="giphy-container">
      {/* Render the NavBar component and pass necessary props */}
      <NavBar
        searchTerm={searchTerm}
        setSearchTerm={setSearchTerm}
        handleSubmit={handleSubmit}
        handleClick={handleClick}
        setStickerData={setStickerData}
        API_KEY={API_KEY}
        setIsError={setIsError}
        setIsLoading={setIsLoading}
      />

      {/* Conditional rendering based on isLoading and isError */}
      {isLoading ? (
        <Spinner />
      ) : isError ? (
        <div className="error-container">
          <UserIcons className="error-icon" icons={FaRegWindowClose} />
          <p className="error-message">
            Oops! Something went wrong. Please try again later.
          </p>
        </div>
      ) : (
        <GifRoute
          giphyData={giphyData}
          loadMoreData={loadMoreData}
          stickerData={stickerData}
          API_KEY={API_KEY}
          searchTerm={searchTerm}
          currentPage={currentPage}
          setStickerData={setStickerData}
          setCurrentPage={setCurrentPage}
          setIsLoading={setIsLoading}
          setIsError={setIsError}
        />
      )}
    </div>
  );
};

export default App;
```

Update the file **NavBar.js** with the following code;

```javascript
import axios from "axios";
import UserIcons from "commons/Icons";
import { FaEllipsisV } from "react-icons/fa";
import Button from "commons/Button";
import SearchBar from "components/SearchBar/SearchBar";
import GiphyLogo from "assets/gif/giphy-logo.gif";
import Svgs from "assets/svgs";
import { Link, useLocation, useNavigate } from "react-router-dom";
import "./NavBar.css";

const STICKER_URL = "https://api.giphy.com/v1/stickers/search";

const NavBar = ({
  searchTerm,
  setSearchTerm,
  handleSubmit,
  handleClick,
  setStickerData,
  API_KEY,
  setIsError,
  setIsLoading,
}) => {
  const menuItems = [
    { title: "Reactions", path: "/" },
    { title: "Entertainment", path: "/" },
    { title: "Sports", path: "/" },
    { title: "Stories", path: "/" },
    { title: "Artists", path: "/" },
    {
      title: <UserIcons icons={FaEllipsisV} />,
      className: "navigation__menu-ellipsis-container",
    },
  ];

  // React Router hooks
  let navigate = useNavigate();
  let location = useLocation();

  // Handle navigation to the home page
  const handleHomeNavigation = () => {
    setSearchTerm("");

    if (location.pathname !== "/") {
      navigate("/");
    }
  };

  // Handle search input and submit for stickers
  const handleStickerSearch = async () => {
    setIsError(false);
    setIsLoading(true);

    try {
      let endpoint = "";

      // the if condition only make a request to the endpoint if searchTerm is truthy
      if (searchTerm) {
        endpoint = `${STICKER_URL}?api_key=${API_KEY}&q=${searchTerm}&_limit=25&rating=Y&lang=en`;
        const results = await axios.get(endpoint);
        setStickerData(results.data.data);
        navigate("/sticker"); // add this line to navigate to the "/sticker" route
      }
    } catch (error) {
      console.error(error);
      setIsError("Oops! Something went wrong. Please try again later.");
    }

    setIsLoading(false);
  };

  return (
    <header className="navigation">
      <nav className="navigation__content">
        {/* Giphy logo */}
        <div
          className="navigation__logo-container"
          onClick={handleHomeNavigation}
        >
          <Svgs.GiphyLogo className="navigation__giphy-logo" />
        </div>

        {/* Small Giphy logo */}
        <img
          className="navigation__gif-small-logo"
          src={GiphyLogo}
          alt="giphy-logo.gif"
          onClick={handleHomeNavigation}
        />

        {/* Menu items */}
        <div className="navigation__menu-container">
          <ul className="navigation__menu-list">
            {menuItems.map((item, index) => (
              <li
                className={
                  item === "UserIcons"
                    ? "navigation__menu-ellipsis-container"
                    : "navigation__menu-list-item"
                }
                key={index}
              >
                <Link
                  className="navigation__menu-link"
                  to={item.path}
                  onClick={item.path ? handleHomeNavigation : null}
                >
                  {item.title}
                </Link>
              </li>
            ))}
          </ul>
        </div>

        {/* Buttons */}
        <div className="navigation__gif--sticker-btn-container">
          <span
            className="navigation__stickers-container"
            onClick={handleStickerSearch}
          >
            <Button className="navigation__stickers-btn" title="stickers" />
          </span>

          <span
            className="navigation__stickers-container"
            onClick={() => {
              navigate("/");
            }}
          >
            <Button className="navigation__stickers-btn" title="GIFs" />
          </span>
        </div>
      </nav>

      {/* SearchBar component */}
      <SearchBar
        searchTerm={searchTerm}
        setSearchTerm={setSearchTerm}
        handleSubmit={handleSubmit}
        handleClick={handleClick}
      />

      {/* Explore section */}
      <div className="navigation__explore-container">
        <p className="navigation__explore-content">
          Explore <span className="navigation__explore-gif-text">explore</span>{" "}
          GIFs
        </p>
      </div>
    </header>
  );
};

export default NavBar;
```

To complete the task, navigate to the components directory, locate the pages directory, and create a new directory named Sticker inside it. Within the Sticker directory, create two files: **Sticker.js** and **Sticker.css**.

Inside the **Sticker.js** add this code;

```javascript
import { useState } from "react";
import axios from "axios";
import PropTypes from "prop-types";
import InfiniteScroll from "react-infinite-scroll-component";
import Spinner from "components/Spinner/Spinner";
import "./Sticker.css";

const STICKER_URL = "https://api.giphy.com/v1/stickers/search";

// Sticker component that displays sticker images with infinite scroll
const Sticker = ({
  stickerData,
  API_KEY,
  searchTerm,
  currentPage,
  setStickerData,
  setCurrentPage,
  setIsLoading,
  setIsError,
}) => {
  const [hasMoreData, setHasMoreData] = useState(true);

  // Function to load more sticker data
  const loadMoreStickerData = async () => {
    try {
      let endpoint = "";

      if (searchTerm) {
        endpoint = `${STICKER_URL}?api_key=${API_KEY}&q=${searchTerm}&limit=25&offset=${
          currentPage * 25
        }&rating=Y&lang=en`;
      }

      const results = await axios.get(endpoint);
      const newData = results.data.data;

      setStickerData((prevData) => [...prevData, ...newData]);

      // Check if there is more data to load
      if (newData.length < 25) {
        setHasMoreData(false);
      }

      setCurrentPage((prevPage) => prevPage + 1);
    } catch (error) {
      setStickerData([]);
      console.log(error);
    } finally {
      setIsLoading(false);
      setIsError(false);
    }
  };

  return (
    <InfiniteScroll
      dataLength={stickerData.length} // Current length of stickerData array
      next={loadMoreStickerData} // Function to load more sticker data
      hasMore={hasMoreData} // Indicates if there is more sticker data to load
      loader={<Spinner />} // Component to display while loading sticker data
    >
      <div className="sticker">
        {/* Render each sticker image in the stickerData array */}
        {stickerData.map((sticker, index) => (
          <div
            className="sticker__image-container"
            key={index}
            onClick={() => window.open(sticker.images.preview_gif.url)}
          >
            <img
              className="sticker__image"
              src={sticker.images.fixed_height.url}
              alt="gif"
            />
          </div>
        ))}
        {/* Display a message when there are no more stickers to load */}
        {!hasMoreData && (
          <p className="sticker__message">No more stickers to load...</p>
        )}
      </div>
    </InfiniteScroll>
  );
};

// PropTypes for type checking the stickerData prop
Sticker.propTypes = {
  stickerData: PropTypes.array.isRequired, // Array of sticker data
};

export default Sticker;
```

and inside the **Sticker.css** add this style;

```javascript
.sticker {
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgb(255, 255, 255);
    border-radius: 0px;
    outline: none;
    margin: 0px;
    padding: 0px;
    border: 0px;
    font: inherit;
    position: relative;
    width: 870px;
    flex-wrap: wrap;
    overflow: hidden;
  }
  .sticker__image-container {
    margin: 8px;
    cursor: pointer;
    color: rgb(255, 255, 255);
    position: relative;
    vertical-align: baseline;
    position: relative;
  }
  .sticker__image {
    color: rgb(255, 255, 255);
    border-radius: 8px;
    height: 200px;
    width: 200px;
  }
  .sticker__message {
    color: rgb(255, 255, 255);
    font-size: 24px;
  }
  
  @media only screen and (min-width: 614px) and (max-width: 844px) {
    .sticker {
      display: flex;
      align-items: center;
      justify-content: center;
      font: inherit;
      position: relative;
      width: 100%;
      max-width: 614px;
      flex-wrap: wrap;
      overflow: hidden;
    }
    .sticker__image-container {
      margin: 8px;
      cursor: pointer;
      color: rgb(255, 255, 255);
      position: relative;
      vertical-align: baseline;
      position: relative;
    }
    .sticker__image {
      color: rgb(255, 255, 255);
      border-radius: 8px;
      height: 188px;
      width: 188px;
    }
  }
  @media only screen and (min-width: 321px) and (max-width: 613px) {
    .sticker {
      display: flex;
      align-items: center;
      justify-content: center;
      font: inherit;
      position: relative;
      width: 100%;
      max-width: 321px;
      flex-wrap: wrap;
      overflow: hidden;
    }
    .sticker__image-container {
      margin: 8px;
      cursor: pointer;
      color: rgb(255, 255, 255);
      position: relative;
      vertical-align: baseline;
      position: relative;
    }
    .sticker__image {
      color: rgb(255, 255, 255);
      border-radius: 8px;
      height: 144px;
      width: 144px;
    }
  }
```

## Implementing Routes
Go back to the GifRoute we created earlier and add the following code;

```javascript
import React from "react";
import { Route, Routes } from "react-router-dom";
import GifGallery from "pages/Giphy/GifGallery";
import Sticker from "pages/Sticker/Sticker";
import ErrorPage from "pages/ErrorPage";

// GifRoute component that defines the routes for GIF-related pages
const GifRoute = ({
  giphyData,
  loadMoreData,
  stickerData,
  API_KEY,
  searchTerm,
  currentPage,
  setStickerData,
  setCurrentPage,
  setIsLoading,
  setIsError,
}) => {
  return (
    <Routes>
      {/* Route for the GifGallery page */}
      <Route
        path="/"
        element={
          <GifGallery giphyData={giphyData} loadMoreData={loadMoreData} />
        }
      />

      {/* Route for the Sticker page */}
      <Route
        path="/sticker"
        element={
          <Sticker
            stickerData={stickerData}
            API_KEY={API_KEY}
            searchTerm={searchTerm}
            currentPage={currentPage}
            setStickerData={setStickerData}
            setCurrentPage={setCurrentPage}
            setIsLoading={setIsLoading}
            setIsError={setIsError}
          />
        }
      />

      {/* Route for the ErrorPage */}
      <Route path="*" element={<ErrorPage />} />
    </Routes>
  );
};

export default GifRoute;
```

## GitHub Code to this project (feel free to give it a star ⭐)

Find below the GitHub Code to this project (feel free to give it a star ⭐):

{% github https://github.com/Willochs316/giphy-api %}

## Follow me

|  |  |  |  |
| --------- |:---------:| -------:| -------:|
| [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bp70sifnip8wuupc49r7.png)](https://dev.to/willochs316) | [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43w3ga66hfvhmyg13f5l.png)](https://github.com/Willochs316) | [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ftky33oi4pkxktvleic.png)](https://www.linkedin.com/in/willochs316/) | [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3am6nfvit6tlnsss5rvp.png)](https://twitter.com/willochs_dev) |