CSS Loader & Spinner Generator

Choose from 108 CSS loaders in 36 families, including rings, arcs, gradient rings, spokes, bouncing and pulsing dots, typing indicators, equalizer bars, pulses and ripples, orbits, clocks, hourglasses, progress bars, striped bars, skeleton screens and loading text. Adjust size, colors, thickness and speed, and copy accessible markup with role="status" and hidden "Loading…" text. Every loader slows down instead of stopping for people who prefer reduced motion.

Preset gallery
Loading…

Accessibility checks

  • Pass The loader has 6.3:1 contrast with the background, so it is easy to spot.
  • Pass role="status" with hidden "Loading…" text tells screen-reader users that something is happening.
  • Pass For people who prefer reduced motion, the animation runs at a quarter of the speed instead of stopping, so the busy cue stays.

Export code

.loader {
  color: #4F46E5;
}

.loader__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.loader {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  box-sizing: border-box;
  border: 4px solid rgba(79, 70, 229, 0.2);
  border-top-color: #4F46E5;
  animation: loader-spin 0.9s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .loader {
    animation: loader-spin 3.6s linear infinite;
  }
}

@keyframes loader-spin {
  to {
    transform: rotate(360deg);
  }
}
Favourites on this device (0)

No favourites yet.

Loader
Variant
Colors
Main color
Second color
Background

How to use the loader generator

  1. Browse the gallery by spinners, dots, bars, pulses, shapes or progress, then select Customize.
  2. Pick a variant. Each family has three, such as more dots, a double ring or a different motion.
  3. Set the size, thickness and speed.
  4. Choose colors. Some loaders use a second color, and the background color drives the contrast check.
  5. Copy CSS and HTML, SCSS, a Tailwind component class or a React component.

The markup

<div class="loader" role="status">
  <span class="loader__sr">Loading…</span>
  <i aria-hidden="true"></i>
</div>

Show the loader when work starts and remove it when it ends. If loading takes long, consider replacing it with a progress message such as “Still working… 60%”.

Worked examples

Example 1: a button that’s saving

Use Ring · Classic at 18px with a 2px thickness and the button’s text color. Place it inside the button before the label, and set aria-busy="true" on the form while saving.

Example 2: a chat app

Use Typing · Bubble: three dots in a soft pill, like messaging apps show while someone types. Keep it small (around 32px) and use your brand’s secondary color.

Example 3: a dashboard loading data

Use Skeleton · Card for each tile while data loads. The layout stays stable, so nothing jumps when content arrives, which helps your CLS score.

Choosing a loader

  • Under 1 second: often no loader is needed. Showing one too quickly can make the app feel slower.
  • 1–10 seconds: a spinner or dots.
  • Longer, measurable work: a determinate progress bar with a percentage.
  • Content with a known layout: a skeleton.

Common mistakes

  • Showing a spinner too soon. For waits under about a second, a loader flashes and feels slower. Delay it by 300–500 ms.
  • No text alternative. Screen reader users need role=“status” with a visually hidden “Loading…” message, as in the markup above.
  • Animating layout properties. Animate transform and opacity, not width or margin, to keep the loader smooth and cheap.
  • Ignoring reduced motion. Slow or stop the animation under prefers-reduced-motion and rely on the text message instead.
  • Spinners for long tasks. If you know the progress, show a progress bar with a percentage; an endless spinner makes long waits feel longer.

Performance

Most loaders animate transform or opacity, which the GPU handles smoothly. A few use background-position or masks, which are still light for a single small element. Avoid running dozens of loaders at once; one per region is enough.

Remember to remove the loader from the DOM when loading finishes, not just hide it visually. Otherwise screen readers may still announce “Loading…”.

Frequently asked questions

How do screen readers know something is loading?

Each loader is a div with role="status" and visually hidden text that says "Loading…". The visual parts are aria-hidden. When the loader appears, screen readers announce the status politely, without interrupting.

What happens for people who prefer reduced motion?

Completely stopping a loader would remove the only sign that the page is working. Instead, the export slows the animation to a quarter of its speed under prefers-reduced-motion, so it's calm but still alive. You can change this to a static icon if you prefer.

Do these loaders need images or JavaScript?

No. They are pure HTML and CSS, using borders, gradients, masks, pseudo-elements and keyframes. Most use one element plus a few aria-hidden parts.

When should I use a skeleton instead of a spinner?

Use a skeleton screen when you know the shape of the content that's coming, such as a list of cards. It makes the wait feel shorter and avoids layout shift. Use a spinner for short, unknown waits.

Can a loader be too fast?

Blinking loaders that flash quickly can be uncomfortable, and above three flashes per second they can trigger seizures. The check warns when an opacity-blinking loader runs too fast.

Sources