Skeleton Loaders: Simplifying Data Loading in React: Part 1
Published: 2024-06-19 20:58:40


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9c14c5lgckohbd8qkhap.png)

## Part One: Creating Our React App
Welcome to our two-part series on Skeleton Loaders: Simplifying Data Loading in React! In part one of this series, we'll create our modern react application from scratch, fetch the data, and add styling.

## Understanding the Power of Skeleton Loading Screens

Many modern websites handle data fetching in the browser instead of on the server. This is good because it means that a user doesn’t have to wait long before getting their content loaded from the server but then has to wait for their data to be loaded from the browser once it arrives. 

Developers often use loaders or spinners to manage user expectations during this data-fetching process. A particularly effective and popular approach, adopted by major websites like Facebook and LinkedIn, is the use of skeleton loading screens. These screens display placeholder elements that mimic the layout of the actual content, providing a visual representation of the incoming data.


## Prerequisites

- Basic knowledge of React.
- Familiarity with React Hooks.

## Setting Up the Project

First, we'll create a new React application using the following command:

```bash
npx create-react-app react-skeleton-screens
```

Next, navigate into the newly created project directory:

```bash
cd react-skeleton-screens
```

Open the project in Visual Studio Code:

```bash
code .
```

## Removing Boilerplate Code

Let's clean up the default files created by `create-react-app`:

1. Open the `src` folder and delete the following files:
   - `App.css`
   - `App.test.js`
   - `logo.svg`
   - `setupTests.js`

2. In `index.js`, remove the import and invocation of the service worker.

3. In `App.js`, remove the import of `logo.svg` and `App.css`.

Replace the code in `App.js` with the following:

```jsx
import React from 'react';
import User from './components/User';
import Articles from './components/Articles';

function App() {
  return (
    <div className="App">
      <header>
        <h1>React Skeletons</h1>
      </header>

      <div className="content">

      </div>
    </div>
  );
}

export default App;
```

## Creating Components

Now, let's create the necessary components. In the `src` directory, create a new folder called `components`. Inside this folder, create one file: `Home.jsx`.

```jsx
import React from 'react';

const Home = () => {
  return (
    <div className="home">
     
    </div>
  );
}

export default Home;
```

With these steps, you've set up a basic React application structure and created components for our landing page, which will serve as the foundation for implementing skeleton screens. This setup ensures a clean slate, allowing you to focus on building the skeleton loading screens that enhance user experience during data fetching.

Now we’re going to nest the Home component inside the content div in our App.js component. Copy the code below into your app.js to do this:

```jsx
import Home from './Home';


function App() {
  return (
    <div className="App">
      <header>
        <h1>Meal Recipes</h1>
      </header>

      <div className="content">
        <Home />
      </div>
    </div>
  );
}

export default App;
```

## Adding Styles to the Application

To enhance the appearance of our application, we'll apply some styles to the header in `App.js`. Follow the steps below to update the styles in your `index.css` file.

### Update `index.css`

Open `index.css` and replace its contents with the following styles:

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


header {
  font-size: 1.5rem;
  font-weight: 900;
  display: grid;
  align-items: center;
}


header h1 {
  max-width: 1200px;
  margin: 0 auto;
}


.container {
  background-color: #6b7280;
  color: #ffffff;
  min-height: 100vh;
  transition-property: all;
  transition-duration: 1s;
  transition-timing-function: ease-out;
}


.meals {
  display: grid;
  grid-template-columns: repeat(1, minmax(0, 1fr));
  gap: 1.25rem; /* Equivalent to gap-5 in Tailwind */
  margin-top: 1.25rem; /* Equivalent to mt-5 in Tailwind */
  transition-property: all;
  transition-duration: 1s;
  transition-timing-function: ease-out;
  padding: 10px 50px;
}


@media (min-width: 640px) {
  .meals {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}


@media (min-width: 768px) {
  .meals {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}


@media (min-width: 1280px) {
  .meals {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}


/* .meal class */
.meal {
  /* Equivalent to rounded overflow-hidden shadow-md cursor-pointer relative h-60 w-60 xs:h-56 xs:w-52 sm:h-56 sm:w-52 lg:h-56 lg:w-52 text-left; */
  border-radius: 0.25rem;
  overflow: hidden;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
    0 2px 4px -1px rgba(0, 0, 0, 0.06);
  cursor: pointer;
  position: relative;
  height: 15rem; /* Equivalent to h-60 */
  width: 15rem; /* Equivalent to w-60 */
}


.meal-img:hover {
  box-shadow: 0 10px 15px -3px rgba(147, 102, 102, 0.1),
    0 4px 6px -2px rgba(0, 0, 0, 0.05);
  transition-property: all;
  transition-duration: 1s;
  transition-timing-function: ease-out;
}


/* .meal-img class */
.meal-img {
  /* Equivalent to w-full h-full border-solid border-4 border-white; */
  width: 100%;
  height: 100%;
  border-style: solid;
  border-width: 4px;
  border-color: #ffffff; /* Assuming white border */
}
```

### Explanation of Styles

1. The universal selector (`*`) is used to apply the following styles to all elements on the page:
   - `margin: 0;` and `padding: 0;` set the margin and padding of all elements to zero, effectively removing any default spacing.
   - `box-sizing: border-box;` ensures that the total width and height of an element include both its content and padding, but not the margin.

2. The `header` element:
   - `font-size: 1.5rem;` sets the font size to 1.5 times the default font size.
   - `font-weight: 900;` makes the text bold.
   - `display: grid;` uses CSS Grid for layout.
   - `align-items: center;` vertically centers the content within the header.

3. The `.container` class:
   - `background-color: #6b7280;` sets the background color to a shade of gray (#6b7280).
   - `color: #ffffff;` sets the text color to white.
   - `min-height: 100vh;` ensures that the container takes up at least the full viewport height.
   - The `transition` properties control the animation duration and timing function when transitioning styles.

4. The `.meals` class:
   - Uses CSS Grid to create a responsive grid layout with varying column numbers based on screen width.
   - `gap: 1.25rem;` adds spacing between grid items.
   - `margin-top: 1.25rem;` provides top margin.
   - The `transition` properties control the animation when transitioning styles.
   - `padding: 10px 50px;` adds padding to the container.

5. Media queries:
   - Adjust the number of columns in the `.meals` grid based on screen width.

6. The `.meal` class:
   - Creates a card-like styling for meal items.
   - Sets border radius, overflow behavior, box shadow, cursor, and dimensions.

7. The `.meal-img` class:
   - Sets the image dimensions and adds a white border.


These styles will ensure that your application's header looks clean and visually appealing. The header will have a consistent blue background with white text, providing a professional look.

### Final Steps

After adding the styles, your header in the application should now have a distinct appearance, with a blue background and centered white text.

### Running the Application

To see your changes in action, make sure to start your development server if it's not already running:

```bash
yarn dev
```

Navigate to `http://localhost:3000` in your browser to view the updated header with the new styles applied.

With these styling updates, your React application now has a polished header, setting a solid foundation for building out the rest of the skeleton loading screens and other features.


Fetching Data

We're going to use the MealDB API for our data fetching: https://www.themealdb.com/api.php.

In our `App.js`, let's create a state to store our data when we fetch it.

```jsx
const [meals, setMeals] = useState(null);
```

Initially, our `meals` state will be `null` because we don't have any meal data yet. However, when this component is rendered to the DOM, we need to fetch the data. To do this, we'll use the `useEffect` hook, which runs automatically after the component has been rendered.

Let's create a `useEffect` and use a `setTimeout` so that we can see the effect of the skeleton loader for a bit longer. Note that we wouldn't typically use a delay like this in a production application. Copy and paste the code below into the `App.js` file:

```jsx
import React, { useState, useEffect } from 'react';
import Home from './components/Home';

function App() {

  const [meals, setMeals] = useState(null);

  // runs automatically after initial render
  useEffect(() => {
    setTimeout( async () => {
      const res = await fetch('https://www.themealdb.com/api/json/v1/1/search.php?s=chicken');
      const data = await res.json();
      setMeals(data);
    }, 5000)
  }, [])


  return (
    <div className="App">
      <header>
        <h1>Meal Recipes</h1>
      </header>

      <div className="content">
        <Home />
      </div>
    </div>
  );
}

export default App;
```

Now we need to check if we have our meal results. Let's use conditional rendering in React to display our meal recipe results. Copy and paste the code below into the `Home.js` file:

```jsx
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom'


const Home = ( ) => {
    const [meals, setMeals] = useState(null);


  useEffect(() => {
    setTimeout(async () => {
      const res = await fetch(
        "https://www.themealdb.com/api/json/v1/1/search.php?s=chicken"
      );
      const meals = await res.json();
      setMeals(meals);
      console.log(meals.meals[0])
    }, 5000);
  }, []);


  return (
    <>
      <div className="bg-gray-900 text-white min-h-screen">
        <div className="m-auto max-w-3xl flex flex-col items-center justify-center text-center">


          <div id="meals" className="meals">
            {meals &&
              meals.meals.map((meal) => (
                <div className="meal" key={meal.idMeal}>
                  <Link to={`/MealInfo/${meal.idMeal}`}>
                    <img
                      className="meal-img"
                      src={meal.strMealThumb}
                      alt={meal.strMeal}
                    />
                    <div className="meal-info" data-mealid={meal.idMeal}>
                      <h3>{meal.strMeal}</h3>
                    </div>
                  </Link>
                </div>
              ))}
          </div>
          ){"}"}
        </div>
      </div>
    </>
  );
};


export default Home;
```

## Adding React Router

Since you're using `Link` from `react-router-dom`, we need to install `react-router-dom` in your project. Follow these steps:

1. Open your terminal or command prompt.
2. Navigate to your project directory.
3. Run the following command to install `react-router-dom` using npm:

```bash
npm install react-router-dom
```

Alternatively, if you're using Yarn, you can use this command:

```bash
yarn add react-router-dom
```

4. Once the installation is complete, you can import the necessary components from `react-router-dom` and start defining your routes in your `Home.js` file.

### Wrapping the App with BrowserRouter
Let’s wrap our App in main.js with BrowserRouter from react-router-dom. This is because we're using Link from react-router-dom in our Home component, which is calling useContext. The context it is looking for is provided by BrowserRouter, but our app is not wrapped by a BrowserRouter. Your main.js should be like this:

```.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'


ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);
```

## Conclusion

In conclusion, in Part One of our series, we've laid the groundwork for implementing skeleton loading screens in React applications. By setting up a basic React project, creating components, adding styles, and fetching data from an API, we've prepared the foundation for integrating skeleton loading screens into our application.

In Part Two, we'll dive deeper into the implementation details of skeleton loading screens. We'll explore how to create reusable skeleton components, customize loading animations, and handle various loading scenarios efficiently. Click [here](https://dev.to/topeogunleye/part-two-creating-our-recipe-reusable-skeleton-component-cgj) for Part Two, where we'll take our skeleton loading screens to the next level!