Skip to content
Corsair UI
Browse the docs

ComponentsNo. 07 of 109

Bars Spinner

Twelve bars around a hub fading in turn, announced as Loading. No JavaScript.

Installation

pnpm dlx shadcn@latest add @corsair-ui/bars-spinner

No setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.

Also adds
utils
npm
clsxtailwind-merge

Examples

Sizes and colours

In a button

API

PropTypeDefault
size

Width and height; or use a size-* class.

number | string—

Accessibility

  • It is role="status", announced as "Loading". Pass a specific aria-label when you can.
  • With prefers-reduced-motion it turns at half speed rather than stopping.

Compatibility

Tailwind CSS
3.4 and 4, both checked in CI
React
19
Rendering
Works in server components

Source

1 file, installed as source you own
bars-spinner.tsxView on GitHub
import type { ComponentProps, CSSProperties } from "react";

import { cn } from "@/registry/default/lib/utils";

const BARS = Array.from({ length: 12 }, (_, index) => index);

interface BarsSpinnerProps extends ComponentProps<"span"> {
  /** Width and height, as a CSS length or a number of px. Or size it with a `size-*` class. */
  size?: number | string;
}

/**
 * Twelve bars around a hub, fading one after another. It takes the current
 * text colour and is announced as "Loading"; pass a more specific
 * `aria-label` when you can ("Syncing charts"). With `prefers-reduced-motion`
 * it turns at half speed rather than stopping, since it reports progress.
 * No JavaScript: it works in server components.
 */
function BarsSpinner({ size, className, style, ...props }: BarsSpinnerProps) {
  return (
    <span
      role="status"
      aria-label="Loading"
      data-slot="bars-spinner"
      className={cn(
        "relative inline-block size-4 shrink-0 [--bars-spinner-period:1s] motion-reduce:[--bars-spinner-period:2s]",
        className
      )}
      style={{ ...(size === undefined ? null : { width: size, height: size }), ...style }}
      {...props}
    >
      {BARS.map((index) => (
        <span
          key={index}
          aria-hidden="true"
          className="animate-bars-spinner absolute top-[45.5%] left-[37%] h-[9%] w-[26%] rounded-full bg-current"
          style={
            {
              transform: `rotate(${index * 30}deg) translate(140%)`,
              animationDuration: "var(--bars-spinner-period)",
              // Negative delays start every bar part-way through, so the wheel turns from the first frame.
              animationDelay: `calc(var(--bars-spinner-period) * ${(index - 12) / 12})`,
            } as CSSProperties
          }
        />
      ))}
    </span>
  );
}

export { BarsSpinner, type BarsSpinnerProps };