DEV Community

Shemona Singh
Shemona Singh

Posted on • Edited on

The Easiest Way to Animate a Loading Spinner

While on a quest to learn how to build some of the most commonly requested animations by designers, the loading spinner seems like a rite of passage.

This time around, I wanted to see if I could use the awesome power of svgs to draw out a circle then animate that circle. This could be a lot cleaner than attempting to animate borders or place rotating circles on top of other circles in CSS.

We will be building today's spinner here with React. Thinking in terms of states, there are two main ones. We are either:

  1. Waiting for something - show the spinner
  2. That something has happened - no longer show the spinner

To make this feel more realistic, we will have the spinner wait for a response from the Fetch api. There are plenty of open apis for us to request from for the sake of this tutorial.

Take a look at the set up for our component.


import React, { useState, useEffect } from 'react';
import './Loading.scss';

export const Loading = () => {
  const [loading, hasLoaded] = useState(false);

  useEffect(() => {
    const timer = setTimeout(() => {
      fetch('https://jsonplaceholder.typicode.com/posts')
        .then((response) => response.json())
        .then((json) => {
          hasLoaded(true);
        });
    }, 1100);

    return () => clearTimeout(timer);
  }, []);

  return (
    <div className="spinner-container">
      {loading ? (
        <p>Content has loaded!</p>
      ) : (
        <svg className="spinner" role="alert" aria-live="assertive">
          <circle cx="30" cy="30" r="20" className="circle" />
        </svg>
      )}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Let's walk through what's going on here.

  1. First, we set up the two states I mentioned at the beginning. The only two states the spinner can be in: either we are waiting for something to happen, or it has already happened. A simple boolean does the trick.
  2. The handy useEffect hook is where we can handle what it is that we're waiting for. It's likely you'll be waiting for some data to return, so I've set up a sample fetch request. You may also notice I have it wrapped inside of a setTimeout. This is because the information comes far too fast for us to see the spinner in action, so for the purposes of delaying our response I've added a setTimeout that you're welcome to adjust in order to see the spinner for longer. I have it set to 1100 milliseconds so that we can see the spinner for at least a second. In reality, you might not need a setTimeout since the data you'll be requesting will likely take it's own time.
  3. In the return of the useEffect, I clean up the setTimeout like the responsible developer I am. 😇
  4. Now for the actual component. We have one div that holds everything. Inside, we set our two states: If the content has loaded already, print something that tells us this. If the content has not yet loaded, this is where we render our animated spinner.
  5. The spinner is a simple circle tag wrapped inside of an svg tag. We define some attributes like height and width, as well as those that will make it accessible like aria-live and role.

Ok! We have the skeleton of a spinner. But, there's nothing to see yet. The styles are where the actual magic happens. Let's take a look at them:

.spinner-container {  
  .spinner {
    transform: rotate(-90deg);
    width: 60px;
    height: 60px;

    .circle {
      stroke-linecap: round;
      stroke-dasharray: 360;
      stroke-width: 6;
      stroke: blue;
      fill: transparent;
      animation: spin .6s ease-in-out 0s normal infinite;
    }
  }

  @keyframes spin {
    from {
      stroke-dashoffset: 360;
    }
    to {
      stroke-dashoffset: 40;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, let's walk through the styles.

  1. We have the .spinner-container wrapped around everything. Pretty straightforward.
  2. The svg gets a class of .spinner with height and width specified as well as the rotation that will occur while being animated.
  3. The .circle class is where I first define some stylistic qualities to the actual circle and then the animation property is the key to it's movement. I set it to be the keyframe animation named spin, which is simply pulling the filling of the circle forward.

Here is what it all looks like in action. Be sure to hit the 'rerun' button on the bottom right.

Voila! Just a few lines of scss to make the spinner come to life. Years ago before I knew this handy tactic of manipulating and animating the fill of svgs, I had built a different spinner. It used bulky, confusing styles to make the drawing of the border for a circle div seem fluid.

Makes you question coding patterns you might be unconsciously following now that could be done more efficiently. 🤔

Top comments (0)