oks-ui

How to use

Loader

Loader is a small, self-contained spinner for indeterminate progress — a request in flight, a section still fetching data, a button mid-submit. It ships with 7 distinct built-in variants rather than one generic spinner, so you can pick whichever visual weight fits the surface it's sitting on.

import { Loader } from "oks-ui";

Picking a variant

All 7 variants (ring-inset, ring-clip, ring-outset, ring-dual, pulse, dots-roll, dots-sweep) are real, distinct animations, not palette swaps of the same shape — ring-inset (the default) reads as a standard spinner; the dots variants read lighter and work well inline next to text; pulse is the least visually busy option for a subtle "something's happening" indicator.

<Loader variant="ring-inset" />
<Loader variant="dots-roll" size="sm" />
<Loader variant="pulse" color="primary" />

Color: one prop or two

color is a shorthand that sets both the track and the moving indicator to the same color in one step — the common case. For a variant where the track and indicator should differ (a light track with a colored indicator, for instance), set trackColor and indicatorColor independently instead of using color.

<Loader color="primary" />

// Independent track/indicator colors
<Loader trackColor="default" indicatorColor="success" />

Sizing and speed

size accepts the usual xs–xl token scale, or a raw pixel number when you need an exact footprint that doesn't map to a token (say, matching an icon's exact size next to it). speed takes any valid CSS time value if the default 1s animation cycle feels off for the context — slower for a background sync, faster for something the user is actively waiting on.

<Loader size="sm" />
<Loader size={18} />
<Loader speed="0.6s" />

Inside a Button

For a loading button, you almost never render Loader directly — pass isLoading to Button and it renders its own internal spinner (with spinnerPlacement/spinner to customize it) already wired to aria-busy. Reach for a standalone Loader when the loading state belongs to a section of the page, not a specific button.

// A loading section, not a button -- standalone Loader
<div className="flex items-center justify-center p-10">
  <Loader label="Loading dashboard" />
</div>

Accessibility

  • Renders with role="status" so assistive technology treats it as a live region announcing an in-progress state.
  • label (defaults to "Loading") supplies the accessible name announced alongside role="status" — override it with something more specific ("Loading dashboard", "Saving changes") whenever there's more than one loading region on a page, so a screen reader user can tell which one just started or finished.

Props reference

All available props for Loader. See the full component page for an interactive playground.

Props

label

string

Default: "Loading"

Accessible label (used with role="status").

Best practices

  • Always keep label meaningful when a page has more than one Loader at once — the default "Loading" text is fine for a single, obvious spinner, but ambiguous if several regions can be loading simultaneously.
  • For a button's own loading state, use Button's isLoading prop instead of manually rendering a Loader inside it.
  • Match variant weight to context: dots-roll/dots-sweep read lighter for inline or small-scale use; ring-inset/ring-outset read as a more standard, higher-visibility spinner for full-section loading states.
  • Don't rely on Loader alone to communicate what's loading — pair it with surrounding text or a label prop, since a lone spinning icon carries no information by itself.
View full API reference for Loader