← 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

If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

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