Loom Loader

Threads drawn through a frame and pulled off the far side, with a shuttle crossing them.

Loading preview

Installation

npx shadcn@latest add @loomui/loom-loader

Usage

import { LoomLoader } from "@/components/ui/loom-loader"
<LoomLoader />

A spinner says only that something is happening. This one says the same thing in the shape of the work. Warp threads drawn down the frame one after another, and a shuttle carrying the weft across them.

The threads are drawn and pulled off rather than faded in and out. A thread that fades is a thread that never went anywhere, and the loop stops reading as progress within about two cycles of watching it.

Size and colour

<LoomLoader className="text-accent size-16" />

The loader is sized entirely by className, and it draws in currentColor, so it inherits the colour of whatever it sits in. Put it inside a button and it is the button's label colour without being told.

Threads carry pathLength="1", which means the dash animation is written in fractions of a line rather than in pixels. The same keyframes hold at size-4 and at size-24, and nothing has to measure the SVG at runtime.

Threads and pace

<LoomLoader strands={7} duration={2000} />

strands is how many warp threads are on the frame. Three reads as a small inline indicator. Seven fills a page-level one. Past about nine the threads are closer together than the stroke is wide and the frame turns into a block.

duration is one thread's whole trip, drawn and pulled off, and it also sets the shuttle's crossing. Each thread starts a fraction behind the one before it. That is what makes five lines read as a weave instead of five lines blinking together.

Props

PropTypeDefaultDescription
strandsnumber5Warp threads on the frame.
durationnumber1400Milliseconds for one thread's trip.
labelstring"Loading"Announced while the loader is on screen.
classNamestringnoneMerged onto the root. Size and colour go here.

Any other <div> prop is forwarded.

Accessibility

The root is a role="status" with an accessible name, so a screen reader is told the page is busy without the SVG being described stroke by stroke. Under prefers-reduced-motion the threads hold fully drawn. A woven glyph rather than an empty frame, because a loader that renders as nothing is worse than one that does not move.