Spinner

CSS Loaders & Spinners: Complete Guide

A CSS loader (also called a CSS spinner or loading animation) is a pure CSS animation that indicates to users that a process is running. Using CSS instead of GIF images or JavaScript libraries means zero extra dependencies, perfect resolution at any screen size, and full control over color, speed and size through custom properties.

Use the generator above to choose from 12 loader styles, dial in your brand color, size and animation speed, then copy the HTML and CSS to drop directly into your project.

How to Use This CSS Loader Generator

  1. Choose a style — click any of the 12 thumbnails: Spinner, Ring, Dots, Bars, Pulse, Ripple, Dual Ring, Flip, Wave, Fade, Squares, or Gradient.
  2. Set your color — pick from the color wheel or type a hex value. The live preview updates instantly.
  3. Adjust size and speed — drag the sliders to set pixel size and animation duration in seconds.
  4. Set thickness — controls the border width on ring-based loaders.
  5. Copy the code — switch between HTML, CSS and combined tabs and paste into your project.

CSS Spinner (Border Trick)

The most common CSS loader uses a circular border with one colored side. The key is setting three sides to a transparent or light color and one side to your brand color, then rotating it with a CSS animation:

.spinner {
  width: 48px;
  height: 48px;
  border: 4px solid rgba(124, 111, 255, 0.2);
  border-top-color: #7c6fff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

CSS Loading Dots

Three bouncing dots are a classic pattern for indicating asynchronous loading. Each dot uses the same animation with staggered animation-delay values to create the wave effect:

.dots {
  display: flex;
  gap: 8px;
  align-items: center;
}
.dots div {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #7c6fff;
  animation: bounce 1.2s ease-in-out infinite both;
}
.dots div:nth-child(1) { animation-delay: -0.32s; }
.dots div:nth-child(2) { animation-delay: -0.16s; }
@keyframes bounce {
  0%, 80%, 100% { transform: scale(0); }
  40% { transform: scale(1); }
}

CSS Bar Loader

Bar loaders (also called equalizer or audio loaders) use scaleY transforms on a row of narrow rectangles. Staggering the delay creates the wave illusion:

.bars {
  display: flex;
  gap: 4px;
  align-items: center;
  height: 48px;
}
.bars div {
  width: 6px;
  height: 100%;
  background: #7c6fff;
  border-radius: 2px;
  animation: bars 1.2s ease-in-out infinite;
}
.bars div:nth-child(1) { animation-delay: -0.3s; }
.bars div:nth-child(2) { animation-delay: -0.2s; }
.bars div:nth-child(3) { animation-delay: -0.1s; }
@keyframes bars {
  0%, 80%, 100% { transform: scaleY(0.4); }
  40% { transform: scaleY(1); }
}

Accessible CSS Loaders

Pure CSS loaders are invisible to screen readers unless you add ARIA attributes. Always wrap your loader with a role and label:

<div role="status" aria-label="Loading">
  <div class="css-loader"></div>
  <span class="visually-hidden">Loading...</span>
</div>

The visually-hidden class hides the text visually while keeping it in the accessibility tree. Use role="status" (polite) for background tasks and role="alert" for time-sensitive updates. See our CSS loader animations deep dive for more on accessibility patterns.

Reducing Motion

Respect users who prefer reduced motion by pausing or replacing animations when the prefers-reduced-motion media query is set:

@media (prefers-reduced-motion: reduce) {
  .css-loader {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

Using CSS Custom Properties for Theming

Parameterize your loader with CSS custom properties so color and speed can be changed from a parent element — perfect for design tokens and dark mode:

.css-loader {
  --loader-color: #7c6fff;
  --loader-speed: 0.8s;
  --loader-size: 48px;

  width: var(--loader-size);
  height: var(--loader-size);
  border: 4px solid transparent;
  border-top-color: var(--loader-color);
  border-radius: 50%;
  animation: spin var(--loader-speed) linear infinite;
}

/* Override per-instance */
.loader-small { --loader-size: 24px; --loader-speed: 0.6s; }
.loader-danger { --loader-color: #e84393; }

For more on this technique, see our CSS custom properties guide.

CSS Loaders vs JavaScript Libraries

Pure CSS loaders have several advantages over JS-based solutions like Lottie or Three.js: no JavaScript bundle size, no runtime dependency, faster first render, and full browser support. The trade-off is that very complex animations (morphing paths, particle systems) are still easier to express in JavaScript. For simple to mid-complexity loaders, CSS is always the right choice.

Want to build more complex animations? See our CSS Animation Generator for keyframe-based animations with full easing control.

Frequently Asked Questions

What is the simplest CSS spinner?

The simplest spinner is a div with a border-radius: 50%, a border where one side is colored and the rest are transparent, and a @keyframes rotate animation. It requires just one HTML element and about 8 lines of CSS with no dependencies.

How do I center a CSS loader on the page?

Wrap it in a container with display: flex; align-items: center; justify-content: center; height: 100vh; for full-screen centering. For inline centering within a button or card, use display: inline-flex; vertical-align: middle; on the loader itself.

How do I change the CSS spinner color?

Change the border-top-color (or border-color for ring loaders) to any CSS color value. Using a CSS custom property like --loader-color: #7c6fff makes it easy to theme across multiple instances.

Can I use CSS loaders in React or Vue?

Yes — the HTML and CSS generated here works in any framework. In React, use className instead of class. In Vue or Svelte, drop the CSS into a <style> block. Using CSS custom properties means you can control the loader from JavaScript via element.style.setProperty('--loader-color', value).

Do CSS loaders work on all browsers?

All loaders generated here use CSS animations (supported in Chrome 43+, Firefox 16+, Safari 9+) and transforms (universal). The gradient spinner uses conic-gradient which requires Chrome 69+, Firefox 83+, Safari 12.1+. All other styles have near-universal support.

How do I stop a CSS loader when content has loaded?

The cleanest approach is to toggle a class that hides the loader: add .hidden { display: none; } to your CSS, then call loaderEl.classList.add('hidden') in JavaScript once your data has loaded. Alternatively, use animation-play-state: paused to freeze rather than hide.

Looking for more animation techniques? Read our CSS loader animations guide or explore the CSS Animation Generator for full keyframe control.

Tool last updated: