CSS Loaders & Tailwind Spinners
Loading animations you can paste anywhere — Tailwind spinners built from animate-spin, animate-bounce, animate-ping and animate-pulse, plus pure CSS loaders with their own @keyframes.
8 components · live preview · copy as HTML or JSX · free for commercial use
8 CSS loaders to copy
Spinner with Label
CSS Loaders
Pure Tailwind (animate-spin). role="status" lets screen readers announce the visible label.
<div role="status" class="inline-flex items-center gap-3 text-sm font-medium text-slate-600">
<span class="h-5 w-5 animate-spin rounded-full border-2 border-indigo-200 border-t-indigo-600"></span>
Loading…
</div>Dual Ring Spinner
CSS Loaders
Pure Tailwind: two opposite colored borders on a spinning circle.
<div role="status">
<span class="block h-10 w-10 animate-spin rounded-full border-4 border-x-transparent border-y-indigo-600"></span>
<span class="sr-only">Loading…</span>
</div>Bouncing Dots
CSS Loaders
Tailwind animate-bounce with staggered [animation-delay] values. Stops for users who prefer reduced motion.
<div role="status" class="flex items-center gap-1.5">
<span class="h-2.5 w-2.5 animate-bounce rounded-full bg-indigo-600 [animation-delay:-0.3s] motion-reduce:animate-none"></span>
<span class="h-2.5 w-2.5 animate-bounce rounded-full bg-indigo-600 [animation-delay:-0.15s] motion-reduce:animate-none"></span>
<span class="h-2.5 w-2.5 animate-bounce rounded-full bg-indigo-600 motion-reduce:animate-none"></span>
<span class="sr-only">Loading…</span>
</div>Pulse Ping
CSS Loaders
Pure Tailwind (animate-ping) — a radar-style pulse that also works as a live indicator.
<div role="status" class="relative flex h-10 w-10 items-center justify-center">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-indigo-400 opacity-75 motion-reduce:animate-none"></span>
<span class="relative inline-flex h-4 w-4 rounded-full bg-indigo-600"></span>
<span class="sr-only">Loading…</span>
</div>Equalizer Bars
CSS Loaders
Also copy the CSS tab — this component needs its custom @keyframes to animate.
<div role="status" class="ldr-bars flex h-8 items-center gap-1">
<div class="h-full w-1.5 rounded-full bg-indigo-600"></div>
<div class="h-full w-1.5 rounded-full bg-indigo-600"></div>
<div class="h-full w-1.5 rounded-full bg-indigo-600"></div>
<div class="h-full w-1.5 rounded-full bg-indigo-600"></div>
<div class="h-full w-1.5 rounded-full bg-indigo-600"></div>
<span class="sr-only">Loading…</span>
</div>SVG Ring Spinner
CSS Loaders
A Material-style growing arc: the SVG rotates while the stroke-dasharray stretches and shrinks. Color follows text-* via currentColor.
Also copy the CSS tab — this component needs its custom @keyframes to animate.
<div role="status" class="inline-flex">
<svg class="ldr-ring h-10 w-10 text-indigo-600" viewBox="0 0 50 50" aria-hidden="true">
<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="5" opacity="0.15" />
<circle class="ldr-ring-arc" cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-linecap="round" />
</svg>
<span class="sr-only">Loading…</span>
</div>Indeterminate Progress Bar
CSS Loaders
role="progressbar" without aria-valuenow tells assistive tech the progress is indeterminate.
Also copy the CSS tab — this component needs its custom @keyframes to animate.
<div role="progressbar" aria-label="Loading" class="relative h-1.5 w-full max-w-sm overflow-hidden rounded-full bg-indigo-100">
<div class="ldr-indeterminate absolute inset-y-0 left-0 w-1/3 rounded-full bg-indigo-600"></div>
</div>Skeleton Card
CSS Loaders
Pure Tailwind (animate-pulse). Match the skeleton to your real card's layout to avoid layout shift.
<div role="status" class="w-full max-w-sm animate-pulse rounded-2xl border border-slate-200 bg-white p-5">
<div class="flex items-center gap-3">
<div class="h-10 w-10 rounded-full bg-slate-200"></div>
<div class="flex-1 space-y-2">
<div class="h-3 w-1/2 rounded bg-slate-200"></div>
<div class="h-3 w-1/3 rounded bg-slate-200"></div>
</div>
</div>
<div class="mt-5 space-y-2">
<div class="h-3 rounded bg-slate-200"></div>
<div class="h-3 w-5/6 rounded bg-slate-200"></div>
</div>
<span class="sr-only">Loading…</span>
</div>Tailwind utilities vs custom @keyframes
Five of these eight loaders need nothing but Tailwind: animate-spin turns a bordered circle into a spinner, animate-bounce with staggered [animation-delay:-0.15s] values makes bouncing dots, animate-ping produces a radar pulse and animate-pulse fades a skeleton card. The others — equalizer bars, the SVG ring spinner and the indeterminate progress bar — ship a short CSS block in the CSS tab. Paste it into your global stylesheet (or a CSS module) along with the HTML; the classes are prefixed ldr- so they will not clash with your own.
To recolor any loader, change the bg-* or border-* color classes. The SVG ring uses stroke="currentColor", so a single text-* class sets its color, and h-* and w-* scale every loader.
Accessible loading states
Every loader has role="status" and visually hidden Loading… text, so screen readers announce it politely without moving focus; the indeterminate bar uses role="progressbar" with an aria-label and no aria-valuenow. Respect users who prefer reduced motion: the bouncing dots and pulse use motion-reduce:animate-none, and the custom CSS loaders slow down under prefers-reduced-motion. Show a loader only after a short delay (around 300 ms) so fast responses don't flash a spinner.
Frequently Asked Questions
How do I make a spinner in Tailwind CSS?↓
Create a circle with h-8 w-8 rounded-full border-4 border-slate-200 border-t-indigo-600 and add animate-spin. No custom CSS is needed.
Do these CSS loaders need JavaScript?↓
No. Every loader on this page animates with CSS alone — either Tailwind's built-in animate-* utilities or the @keyframes in the CSS tab. You only need JavaScript to show and hide the loader while data loads.
When should I use a skeleton instead of a spinner?↓
Use a skeleton when you know the shape of the content that is coming, such as a card or a list, because it prevents layout shift. Use a spinner for short, shapeless waits such as a button submit.
Last updated: