GitHub

Spokes

A segmented spinner for lightweight indeterminate loading states.

import { Spokes } from "@/components/loading-ui/spokes";

export function SpokesDemo() {

Installation

pnpm dlx shadcn add @loading-ui/spokes
components/spokes.tsx
import { cn } from "@/lib/utils";

function Spokes({ className, ...props }: React.ComponentProps<"svg">) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      className={cn("animate-spin", className)}
      {...props}
    >
      <path
        d="M12 2V6M16.2 7.8L19.1 4.9M18 12H22M16.2 16.2L19.1 19.1M12 18V22M4.9 19.1L7.8 16.2M2 12H6M4.9 4.9L7.8 7.8"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

export { Spokes };

Usage

import { Spokes } from "@/components/spokes";
<Spokes className="size-4" />

Why use it

Use Spokes when you want a loader that feels a bit lighter and more technical than a continuous ring. It works especially well in compact UI where a minimal silhouette is easier to read than a fuller circular stroke.

Examples

Compact pending state

import { Spokes } from "@/components/spokes";
 
export function RefreshLabel({ isRefreshing }: { isRefreshing: boolean }) {
  return (
    <div className="inline-flex items-center gap-2 text-sm text-muted-foreground">
      {isRefreshing ? <Spokes className="size-4" /> : null}
      <span>{isRefreshing ? "Refreshing data" : "Up to date"}</span>
    </div>
  );
}

In an input or toolbar

import { Spokes } from "@/components/spokes";
 
export function SearchStatus() {
  return (
    <div className="flex items-center gap-2 rounded-md border px-3 py-2">
      <Spokes className="size-4 text-muted-foreground" />
      <span className="text-sm text-muted-foreground">Searching...</span>
    </div>
  );
}

Size and color

Like Ring, Spokes inherits currentColor.

<Spokes className="size-4 text-muted-foreground" />
<Spokes className="size-6 text-foreground" />
<Spokes className="size-8 text-primary" />

Accessibility

Treat Spokes as a visual indicator, not a complete status message. Pair it with descriptive text or a screen-reader-only label when users need additional context.

Customization

Because the component is an SVG with simple line segments, it is easy to adapt:

  • adjust line caps for a sharper or softer look
  • change stroke width to fit denser interfaces
  • replace the default spin animation with a slower or stepped rotation
  • Ring for a fuller circular loader