Guides · · Maxwell Prehoda
12 Loading Animation Examples From Real Design Systems and CSS Libraries (With Live Demos)
The best loading animations are small, honest and placed exactly where the content will appear. A spinner inside the button you just pressed beats a giant animation in the middle of the screen. The examples below show how GitHub, Atlassian, Vercel, IBM and Google handle in-product loaders, alongside the CSS loader libraries most developers actually copy from.
Every entry was checked against its live docs or repository in November 2026, and each one gets a Best for, a platform line and a caveat. Most entries play a live demo from our Loaders directory: our own pure-CSS take on the same pattern, which you can copy as plain HTML and CSS. This page covers component loaders (spinners, dots, inline states). For full-page preloaders and splash screens, see website loading screen examples.
1. SpinKit by Tobias Ahlin: Best for a classic spinner with zero dependencies
Platform: Pure CSS, MIT license, npm or copy-paste
SpinKit is a long-running collection of CSS spinners: 12 spinners named Plane, Chase, Bounce, Wave, Pulse, Flow, Swing, Circle, Circle Fade, Grid, Fold and Wander. Each one is plain HTML and CSS keyframes, and the spinners share no classes or animations, so you can copy only the one you need instead of the whole stylesheet.
Version 2 made them configurable through two custom properties, `--sk-size` and `--sk-color`, and the README recommends sticking to `transform` and `opacity` animations for smoothness. Defaults are 40px and `#333`, so plan to retint them to your brand.
Our Folding Cube Loader below is a pure-CSS version of SpinKit's Fold idea, rebuilt with our own `--ldr-color` and `--ldr-size` properties. If you want the same look with a reduced-motion guard already attached, it is a direct alternative.
Watch out for: The demo page examples use `margin: 100px auto` and hard-coded colours, so a straight copy adds big gaps around the spinner. Strip the margin and add `role="status"` plus a label yourself.
2. Loaders.css by Connor Atherton: Best for dot and bar loaders that only animate cheap properties
Platform: Pure CSS, MIT license, npm
Loaders.css calls itself "delightful and performance-focused pure css loading animations", and it has earned its 10,000 plus GitHub stars. You include `loaders.min.css`, add an element with a class like `ball-pulse`, and put the right number of child divs inside it. Colour comes from styling the child divs and size from a 2D scale transform.
The ball and line families are still some of the cleanest small loaders around. Our Pulsing Dots Loader below is a pure-CSS take on the same ball-pulse rhythm: three dots that scale in sequence and never change position.
Watch out for: Each animation needs an exact number of child divs, and the optional jQuery helper that injects them is a dependency most projects no longer want. The stated browser targets (IE 11, Chrome 41) show the age of the code.
3. CSS Loaders by Temani Afif: Best for unusual loaders that are still a single div
Platform: Pure CSS, single element, click to copy
Temani Afif's site advertises over 600 CSS-only loaders, every one built on a single `<div class="loader">` with no extra markup. They are sorted into 44 categories with counts, from "The classic" (40) and "The dots" (50) to "The factory" (8), and you click any loader to copy its CSS.
This is the place to go when the usual ring and three dots feel generic and you still want one element in your markup. The tricks are gradients, masks and box-shadows doing the work of extra elements. Our Box Shadow Glow Spinner below uses the same single-element idea: eight dots that are all box-shadows on one empty div.
Watch out for: The site does not state a license, so check with the author before shipping in a commercial product. Dense gradient and mask code is also harder to retint than a loader with named custom properties.
4. LDRS by Griffin Johnston: Best for React or web component projects that want npm loaders
Platform: Web components + React, MIT license, npm
LDRS (from uiball) packages 44 loaders, including Ring, Tailspin, Squircle, Hourglass, Newton's Cradle and Miyagi, as web components and React components. You `npm install ldrs`, import one loader such as `ldrs/ring`, and drop in `<l-ring size="60" color="coral">`. React exports arrived in v1.1.3, with the CSS imported separately per component.
The designs sit in a sweet spot: more character than a plain ring, still calm enough for a real product. Our Newton's Cradle below is a pure-CSS take on the same desk-toy idea. If you would rather not add a package at all, the plain HTML and CSS version is an alternative.
Watch out for: The README says web components only run client-side, so they must be excluded from server rendering (it links Nuxt and Astro guides). Until a component is registered it renders as an empty element.
5. Epic Spinners by Epicmax: Best for Vue 3 apps that want spinners as components
Platform: Vue 3 components, MIT license, npm
Epic Spinners is a set of 20 spinner components from the Vue agency Epicmax, installed with `npm install --save epic-spinners`. The README credits CodePen spinners by @martinvd as the starting point, and the gallery also hands out each spinner as a plain HTML and CSS snippet when you click it.
The set leans orbital and geometric, with atoms, orbits and hollow dots, which suits dashboards that want a bit more motion than a ring. Our Atom Loader below is a pure-CSS take on the orbiting-electron pattern, with three tilted orbits traced by nested translate animations.
Watch out for: The components are Vue 3 only. In React, Svelte or plain HTML you are copying the CSS snippets by hand, so you lose the props for size and colour.
6. GitHub Primer Spinner: Best for product UI where fast requests should not flash a spinner
Platform: Primer React component (GitHub's design system)
Primer describes its Spinner as "an indeterminate loading indicator, ideal for processes with unknown or variable durations". It comes in small, medium and large, and it has two props worth stealing for any loader you build.
The first is `srText`, which defaults to "Loading" for screen readers and can be set to `null` when a visible text node already says what is happening. The second is `delay`: `'short'` waits 300ms before rendering and `'long'` waits 1000ms. That delay is what stops the spinner from flickering on requests that finish in 200ms. Our Circle Notch Spinner below is a pure-CSS take on the same three-quarter ring.
Watch out for: A delay hides the spinner on fast responses but does nothing for slow ones. Pair it with real copy ("Loading pull requests") once a wait passes a few seconds.
7. Atlassian Design System Spinner: Best for deciding where a spinner goes and when it shows
Platform: Atlassian Design System (React)
Atlassian's usage page is one of the clearest sets of placement rules any big product team has published. Show a spinner only when the wait is expected to exceed one second. Use one spinner per page. Put it where the loading happens, so a section that updates gets its own spinner, and if in doubt put it where attention should land when loading finishes.
For actions it points to a `LoadingButton` instead of a free-floating spinner, and when a spinner sits over content it dims that content with an `opacity.loading` token. Our Button with Loading Spinner below is a pure-CSS version of the in-button pattern: a disabled button whose spinner inherits the label colour.
Watch out for: Atlassian deliberately keeps spinners running under reduced motion, because a frozen spinner looks like a stalled process. If your loader stops completely for those users, show text instead so they still know work is happening.
8. Vercel Geist Loading Dots: Best for inline status text like Saving or Deploying
Platform: Geist design system (React)
Geist's Loading Dots are meant for short, indeterminate waits and sit inside copy, as in "Saving" followed by the dots. The guidance is unusually specific: keep the label tied to the actual work (saving, deploying, uploading), never leave dots after the action has finished, and use the `loading` prop on Button rather than putting dots in the button label.
On accessibility, the dots are treated as decorative. The wrapping element gets `aria-live="polite"`, the text carries the meaning, and you should not add an `aria-label` to the dots themselves. The docs also say to respect `prefers-reduced-motion` and never pair the dots with a second animated indicator on the same line. Our Flashing Dots Loader below is a pure-CSS take: nothing moves or resizes, so it is safe beside text.
Watch out for: Geist sends you to Skeleton for layout placeholders and Progress for measurable work. Dots on a page-sized wait read as an afterthought.
9. IBM Carbon Loading and Inline Loading: Best for enterprise forms with save and submit states
Platform: Carbon Design System (React, Web Components)
Carbon splits loaders by scope. The large indicator is for full-screen takeovers, page sections, modals and tiles, usually over a semi-transparent overlay. The small one is for contextual loading next to a button during a save or upload, with no overlay, and related buttons such as Cancel are disabled while it runs.
Two rules stand out: Carbon shows an indicator only when the wait is expected to exceed three seconds, and it says not to show more than one at a time. It also prefers skeleton states for full-screen loads. Our Autosave Indicator Spinner below is a pure-CSS version of the inline pattern, rolling from Saving to Saved with the spinner turning into a check.
Watch out for: Atlassian says one second, Carbon says three. Neither is wrong, but pick one threshold for your product and apply it everywhere, or users get spinners on some screens and dead air on others.
10. Material 3 Expressive loading indicator: Best for Android apps and waits under five seconds
Platform: Jetpack Compose (Material 3), Android
Google's Material 3 Expressive update added a new loading indicator: a looping shape morph through seven Material 3 shapes. Google recommends it for progress that loads in under five seconds, and kept the updated linear and circular progress indicators for longer work. It ships contained (a circular container with a lighter fill, as in pull-to-refresh) and uncontained.
It is the most visible sign that big design systems are moving away from the plain ring toward loaders with personality. In Compose it is `LoadingIndicator` and `ContainedLoadingIndicator`. Our Polygon Morph Loader below is a pure-CSS take on shape morphing: a clip-path that steps from triangle to hexagon and back.
Watch out for: In Compose the API sits behind `@OptIn(ExperimentalMaterial3ExpressiveApi::class)`, so expect signature changes between releases. On the web, clip-path morphs only tween cleanly when every polygon has the same number of points.
11. Our Spinner to Checkmark Loader: Best for save and upload flows that should end in a visible success
Platform: Motions directory, free, pure CSS + inline SVG Ours
Most loaders only cover the waiting. This one also covers the ending: an arc spins, closes into a full circle, pops, and draws a checkmark. It is one SVG circle and one path, animated with `stroke-dasharray` and `stroke-dashoffset`, so it scales cleanly at any size.
Use it on Save, Publish or Upload buttons where people want proof the action landed. Like every snippet in our directory it is plain HTML and CSS, tinted and sized with `--ldr-color` and `--ldr-size`, and the copied code includes a `prefers-reduced-motion` block that slows the loop to three seconds instead of freezing it.
Watch out for: As shipped, it loops through spin and check on a 3.2 second cycle for preview. In production, split the keyframes into a spinning state and a success state and switch them when your request actually resolves.
12. Our Spokes Spinner: Best for web apps that should feel native on Mac and iPhone
Platform: Motions directory, free, pure CSS Ours
Eight rounded spokes fade in turn around a centre point, the activity indicator pattern people know from iOS and macOS. Nothing rotates: each spoke only changes opacity on a staggered delay, which is about as cheap as animation gets for the browser.
Pick it for Electron apps, Mac-first SaaS and anything that sits beside native controls. It is one of more than 300 entries in our Loaders directory, which also has collections for CSS spinners, dot loaders and 3D loaders.
Watch out for: Below about 16px the spokes merge into a grey blob. For tiny inline spots use the flashing dots above, and for Android or Windows users a ring reads as more familiar.
How to choose a loading animation
Three questions settle almost every loader decision. Answer them in this order.
Match it to the wait
Jakob Nielsen's response time limits still hold: under 0.1 seconds feels instant and needs nothing, around 1 second is where people notice the delay, and past about 10 seconds you need a percent-done indicator and a way to cancel. In practice: delay the spinner like GitHub Primer does so fast requests never show one, use a small indeterminate loader (Atlassian's ring, Geist's dots, Material's morphing shape) for the one to five second range, and switch to real progress or a skeleton screen beyond that. Measurable work belongs to a progress bar, not a spinner.
Match it to the spot
Put the loader where the result will appear. A button that saves gets a spinner inside it (Atlassian's LoadingButton, our Button with Loading Spinner). A status line gets dots inside the text (Geist). A section that refreshes gets one centred loader in that section, never a second one elsewhere on the page. Size follows the spot too: dots and rings survive at 16px, characterful loaders like Newton's Cradle or the atom need 40px or more to read.
Match it to your stack and your users
Vue 3 points to Epic Spinners, React or web components to LDRS, and anything else to copy-paste CSS (SpinKit, Loaders.css, CSS Loaders or our Loaders directory). Whichever you pick, give it `role="status"` and a label or a polite live region, animate only `transform` and `opacity`, and decide what happens under `prefers-reduced-motion`: slow it down, as our snippets do, or replace it with text. A frozen spinner is the one option that is always wrong.
Ship a better loader this afternoon
Pick the spot first, then the wait threshold, then the look. For most products that means a delayed ring or dots in the place the content will land, a label screen readers can hear, and a reduced-motion rule. Copy one of the live demos above, set `--ldr-color` to your brand colour, and you are done in minutes.
If none of them fit your brand, describe the loader you want and our creator will build it as production-ready CSS keyframes. You can also paste your own button or card so the loader is made for that component.
Make a loading indicator for a Save button in a project management app. Show it only if the request takes longer than 400ms, then spin a 16px ring inside the button in the label colour. When the request resolves, close the ring and draw a checkmark over 300ms with ease-out, hold for 800ms, then return to the label. Animate only transform, opacity and stroke-dashoffset. Under prefers-reduced-motion, swap the spin for the text Saving and show the check without the draw.
Common questions
What is a loading animation?
A loading animation is a small looping motion, such as a spinner, pulsing dots or a morphing shape, that tells people the interface is working on their request. Indeterminate loaders say "working" without saying how long. When you can measure progress, a determinate progress bar is the better choice.
When should you show a loading spinner?
Only when the wait is long enough to notice. Atlassian shows spinners only for waits over one second, IBM Carbon for waits over three seconds, and GitHub Primer offers a 300ms or 1000ms delay so fast requests never flash a spinner. Past about ten seconds, Nielsen Norman Group recommends a percent-done indicator instead.
Spinner, skeleton or progress bar: which should I use?
Use a spinner or dots for short, unmeasurable waits tied to one action or section. Use a skeleton screen when you are loading a known layout, like a feed or a table, so people see the shape of what is coming. Use a progress bar when you can measure the work, like an upload or an export.
How do I make a loading animation accessible?
Give the loader `role="status"` and a label, or wrap it in an element with `aria-live="polite"` and let visible text carry the meaning, as Vercel's Geist recommends. Animate only transform and opacity, and handle `prefers-reduced-motion` by slowing the loop or showing text. Avoid freezing it entirely, because a stopped spinner looks like a stalled process.
How do you make a simple CSS loading spinner?
Make a square element with `border-radius: 50%`, a faint border all round and a coloured `border-top-color`, then rotate it with a keyframe that goes `to { transform: rotate(360deg); }` on a linear, infinite loop of about 0.8 seconds. Our Ring Spinner is exactly that, in one div.
Do loading animations slow down a website?
A CSS loader that animates `transform` or `opacity` costs almost nothing, because the browser can run it on the compositor. The real cost comes from heavy GIFs, large Lottie files or JavaScript loaders that have to download before they can appear, which is backwards for something meant to cover a wait.
Sources
- SpinKit README (GitHub)
- Loaders.css README (GitHub)
- CSS Loaders by Temani Afif
- LDRS README (GitHub)
- Epic Spinners README (GitHub)
- Primer Spinner component
- Atlassian Design System: Spinner usage
- Vercel Geist: Loading Dots
- Carbon Design System: Loading usage
- 9to5Google: Material 3 Expressive loading indicator (May 2025)
- Nielsen Norman Group: Response Times, the 3 important limits