Loading: Jumping dots

Matt Perry

In this tutorial, we're going to build the Loading: Jumping dots example step-by-step.

This tutorial is rated beginner difficulty, which means we'll spend some time explaining the Motion APIs that we've chosen to use (and why), and also any browser APIs we encounter that might be unfamiliar to beginners.

Here's a live demo of the example we're going to be creating:

Loading...

Introduction

The Jumping dots example shows how to create an animated loading indicator with three dots that bounce up and down in sequence. This playful animation pattern is perfect for loading states where you want to add some personality.

This example uses variants to define the jump animation and staggerChildren to create the wave effect as each dot animates in sequence.

Get started

Let's start with a basic component structure:

"use client"

function LoadingJumpingDots() {
    return (
        <div className="container">
            <div className="dot" />
            <div className="dot" />
            <div className="dot" />
        </div>
    )
}

The dots are styled as small circles:

.container {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 10px;
}

.dot {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: var(--hue-1);
}

Let's animate!

Import from Motion

Import motion and the Variants type:

import { motion, Variants } from "motion/react"

Define the jump animation

Create a variants object that defines the jump animation:

const dotVariants: Variants = {
    jump: {
        transform: "translateY(-30px)",
        transition: {
            duration: 0.8,
            repeat: Infinity,
            repeatType: "mirror",
            ease: "easeInOut",
        },
    },
}

The transform: "translateY(-30px)" moves each dot 30 pixels upward. The repeatType: "mirror" makes the animation play in reverse after completing, creating a smooth up-and-down bounce.

Why use transform directly?

We're using transform: "translateY(-30px)" instead of the shorthand y: -30. Animating the transform property directly enables hardware-accelerated animations, which run on the GPU rather than the CPU. This is especially important during loading sequences, as loading is typically a time of heavy CPU load. By offloading the animation to the GPU, the dots remain smooth even when the main thread is busy. Learn more about web animation performance.

Apply the animation with stagger

Convert the elements to motion components and apply the staggered animation:

function LoadingJumpingDots() {
    return (
        <motion.div
            animate="jump"
            transition={{ staggerChildren: -0.2, staggerDirection: -1 }}
            className="container"
        >
            <motion.div className="dot" variants={dotVariants} />
            <motion.div className="dot" variants={dotVariants} />
            <motion.div className="dot" variants={dotVariants} />
        </motion.div>
    )
}

The staggerChildren: -0.2 creates a 0.2 second delay between each dot's animation start. The staggerDirection: -1 reverses the order so the last dot starts first.

Conclusion

We've built an engaging jumping dots animation using Motion's variant and stagger systems. The repeatType: "mirror" creates smooth bouncing, while staggerChildren orchestrates the wave effect across all three dots.

Motion is supported by the best in the industry.