Unicode Loading Spinners for React, Framer and Any Website
Not every loader needs an SVG or a GIF. A spinner made of text characters, like braille dots turning in a cell, is crisp at any size, takes the color of the text around it, and fits inline in a button, a chat bubble or a table cell. This post covers what to get right and how to add them to React, Framer, Webflow, or plain HTML.
Why text spinners
They sit in a line of text, scale with font-size, and follow currentColor, so dark mode just works. The pitfalls are real, though. Check these:
- No layout shift. Every frame should have the same width, in a monospace cell.
- Screen readers. Hide the frames, announce a label, and announce when the work finishes.
- Reduce Motion. Show a still frame.
- Fonts. The characters come from the visitor's own fonts, so test on your target systems.
Unicode Spinners Pro
Unicode Spinners Pro is a finished set of components for this. Per the docs it has 40 spinners in 7 categories: classic braille, braille grids, AI and streaming, block elements, box drawing, arrows, and geometric shapes. It also has progress bars and AI loaders.
If you only need raw frame data for a terminal or your own loop, the free unicode-animations npm package (MIT, 18 braille spinners) is the right tool. The 18 spinners from it are included in the paid pack unchanged, which adds the 22 other spinners, finished components, the bars, and the accessibility and layout work.
Plain HTML
Upload unicode-spinners.min.js, add the script once, and use the tags:
<script src="/js/unicode-spinners.min.js" defer></script>
<pf-spinner name="helix" label="Generating…"></pf-spinner>
<pf-spinner name="shimmer" label="Thinking…" shimmer-label color-mode="gradient" colors="#0ea5e9, #22d3ee"></pf-spinner>
<pf-progress value="42" label="Uploading" eta></pf-progress>
Then drive them from your code:
const s = document.querySelector("pf-spinner")
s.done("Generated") // ✓ Generated (announced to screen readers)
s.fail("Something broke") // ✗ Something broke
s.start("Trying again…") // spinning again
const bar = document.querySelector("pf-progress")
bar.value = 64 // percent and time left update
bar.value = null // indeterminate
AI thinking and streaming loaders
Three spinners are designed for AI interfaces: shimmer (a "Thinking…" label with a light sweeping across it), typing, and cursor (a token-stream cursor that sits at the end of streamed text). In React there's also an AgentSteps component: multi-step rows whose spinner turns into ✓ or ✗ when each step finishes.
React and Next.js
Copy react/UnicodeSpinners.tsx into your project (React 18+, no other dependencies). The server renders a still frame and the label, and the animation starts in the browser.
import { Spinner, Progress, AgentSteps, useSpinner } from "./components/UnicodeSpinners"
<Spinner name="orbit" state={done ? "done" : "spinning"} label="Searching" doneLabel="Searched 12 sources" />
<Progress value={percent} label="Uploading" eta variant="braille" />
<AgentSteps
steps={[
{ label: "Search the docs", state: "done", result: "Read 12 pages" },
{ label: "Run the tests", state: "running" },
{ label: "Write the answer", state: "pending" },
]}
/>
function SaveButton() {
const frame = useSpinner("orbit") // the current frame as a string
return <button disabled>{frame} Saving</button>
}
Framer
- In Framer, go to Assets → Code → New code file, name it
UnicodeSpinner, and paste inframer/UnicodeSpinner.tsx. - Drag Unicode Spinner onto the canvas.
- In the properties panel choose Spinner or Progress Bar, then the spinner, label, colors, size and font.
On the canvas it shows a still frame; it animates in Preview and on the published site.
Webflow and other site builders
For Webflow, paste script/webflow/footer-code.html into Site settings → Custom code → Footer code, add an Embed element, and put a tag in it:
<pf-spinner name="orbit" label="Loading"></pf-spinner>
Publish: custom code runs on the published site, not in the Designer, and needs a paid site plan. The download also has quick starts for WordPress, Shopify, Squarespace and Wix.
Progress bars and custom frames
Bars come in five styles: blocks (eight steps per cell), braille (six), shade (four), line (two), and ascii (one), at a width of 4 to 120 cells. Leave out value for an indeterminate bar.
You can supply your own frames as a string, with frames split by spaces or |, or as an array in JavaScript and React:
<pf-spinner frames="◐◓◑◒" label="Syncing"></pf-spinner>
Accessibility and performance
- A spinner is
role="status"with a polite live region andaria-busywhile it spins. The frames are hidden from screen readers, who hear the label or a visually hidden "Loading". - Bars are
role="progressbar"with the value spoken as text. - With Reduce Motion on, you get a still frame by default, or
reduced-motion="slow". - The script is 8 KB gzipped with no dependencies. One animation loop drives every spinner on the page, and spinners pause offscreen and in background tabs.
When not to use it
- Where your brand needs a custom illustrated loader.
- If your fonts lack braille or block glyphs. No fonts are bundled.
See it in context
Unicode Spinners Pro is part of the Parallel Studio set on /effects. The Unicode Spinners page shows every spinner running, so you can pick by eye.
Pricing
Unicode Spinners Pro is $9 one-time for one website or project, with free updates to v1.x and a 14-day refund. The whole catalog is available through the All-Access Pass for $49.
Start with one spinner in one button, give it a label, and test it with a screen reader and Reduce Motion on.