← Back toCSS Animation

Loading spinner

Written byPhuoc Nguyen
03 Oct, 2022
Spinner lets users know that a particular task is being processed.


A spinner needs only a single element:
<div class="loading-spinner"></div>
Before diving into the animation, imagine that the spinner has the same width and height:
.loading-spinner {
height: 4rem;
width: 4rem;
At that moment, it looks like a square. The top side should have a different color:
.loading-spinner {
border: 4px solid #d1d5db;
border-top-color: #3b82f6;
It's the right time to turn its shape into a circle by using the border-radius property. The top side then becomes one of four curves:
.loading-spinner {
border-radius: 50%;


To make the curve move continuously, we will rotate it around itself:
@keyframes spinner {
from {
transform: rotate(0deg);
to {
transform: rotate(360deg);
The final step is to use the animation an infinite number of times:
.loading-spinner {
animation: spinner 800ms linear infinite;

Using SVG circles

There is another approach that is more complex using SVG `circle` elements. Let's start with constructing the spinner with two circles:
<svg class="svg-spinner">
<!-- The track circle -->
<circle class="svg-spinner__track"></circle>

<!-- The progress circle -->
<circle class="svg-spinner__progress"></circle>
The diameters of both circles are the same as the spinner's size:
.svg-spinner__progress {
stroke-width: 4px;

/* Assume that the spinner has the size of 64px */
cx: 32;
cy: 32;
r: 32;
Rather than covering the full round of spinner, the progress circle only covers a quarter corner. Using the `dasharray` and `dashoffset` properties allows us to do that:
.svg-spinner__progress {
/* Math.PI * size */
stroke-dasharray: 201.062;

/* (1 - 0.25) * Math.PI * size */
stroke-dashoffset: 150.796;
I leave the formulations here, so you can calculate the styles dynamically based on the spinner's size.
Using the `spinner` animation in the previous section gives us the same result:
.svg-spinner {
animation: spinner 800ms linear infinite;
Last but not least, the SVG element must come with a `viewBox` attribute to indicate the bounding:
<svg class="svg-spinner" viewBox="-2 -2 68 68">...</svg>
The magic numbers `-2 -2 68 68` actually is the resulf of the formulation:
-(strokeWidth / 2) -(strokeWidth / 2) (strokeWidth + size) (strokeWidth + size)
The following table describes the variable used in the formulation:
`strokeWidth`The border of circles (4)
`size`The spinner size (64)

See also

Questions? 🙋

Do you have any questions? Not just about this specific post, but about any topic in front-end development that you'd like to learn more about? If so, feel free to send me a message on Twitter or send me an email. You can find them at the bottom of this page.
I have a long list of upcoming posts, but your questions or ideas for the next one will be my top priority. Let's learn together! Sharing knowledge is the best way to grow 🥷.

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn