59 / Feedback
Spinner
The smallest way to say something is happening, for a control that is working.
"use client"
import { Spinner } from "mischief-ui/spinner"
export function SpinnerDemo() {
return (
<div className="flex flex-wrap items-center gap-8">
<Spinner size={14} />
<Spinner size={20} />
<span className="text-muted-foreground inline-flex items-center gap-2 text-sm">
<Spinner size={14} />
Checking the registry
</span>
<button
type="button"
disabled
className="border-border inline-flex min-h-9 items-center gap-2 rounded-full border px-3 text-sm opacity-70"
>
<Spinner size={13} label="Publishing" />
Publishing
</button>
</div>
)
}Installation
Copy the source into your project, or keep it behind a package.
npx shadcn@latest add Tinkerers-Labs/mischief-ui/spinnerimport { Spinner } from "mischief-ui/spinner"Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.
import * as React from "react" import { cn } from "@/lib/utils" export type SpinnerProps = Omit< React.HTMLAttributes<HTMLSpanElement>, "children"> & { size?: number /** * Announced while it turns. Without one the spinner is decoration, which is * right when the text beside it already says what is happening. */ label?: stringUsage
export function Save({ saving }) {
return (
<button disabled={saving}>
{saving ? <Spinner size={14} /> : null}
{saving ? "Saving" : "Save"}
</button>
)
}Spinner or skeleton
A spinner says work is under way. A skeleton says content has not arrived and holds its place. Reach for the spinner when something you pressed is working, and for the skeleton when a region is filling in -- a spinner in the middle of an empty page tells someone to wait without telling them what for.
Give it a label only when nothing beside it already says what is happening. Two announcements of the same wait is one too many.
API
sizenumberWidth and height in pixels. Defaults to 16.labelstringAnnounced while it turns. Without one it is decoration.classNamestringClasses for the element. Colour comes from currentColor.Accessibility
With a label it is a status region and announces itself once; without one it is hidden from assistive technology, which is right when the text beside it already says what is happening. It draws in currentColor, so it inherits whatever it sits in. Reduced motion stops the turn and leaves the ring, so a control still reads as busy rather than as an unexplained circle.