# Loader Orbit Dots — React Loader Component

> Dots chase each other around a ring, bunching at the top and fanning out as they fall, around a softly pulsing core.

- Category: [Loaders](https://pixelpapa.com/components/loaders.md)
- Tags: loader, spinner, orbit, dots, loading
- Dependencies: none (plus React and Tailwind CSS v4)
- Last updated: 2026-11-13
- Live preview: https://pixelpapa.com/components/loaders/loader-orbit-dots

## Install

```bash
npx shadcn@latest add "https://pixelpapa.com/r/loader-orbit-dots.json"
```

The file is written to `components/pixelpapa/loader-orbit-dots.tsx`. To install manually, copy the source below into your project. It imports `cn` from `@/lib/utils`, which every shadcn project has.

## Source

```tsx
"use client";

import type { CSSProperties } from "react";
import { cn } from "@/lib/utils";

type LoaderOrbitDotsProps = {
  /** Diameter in px. */
  size?: number;
  /** Number of orbiting dots. */
  dots?: number;
  /** Seconds per revolution. */
  speed?: number;
  /** Show the pulsing core. */
  core?: boolean;
  /** Accessible label. */
  label?: string;
  className?: string;
};

export function LoaderOrbitDots({
  size = 48,
  dots = 5,
  speed = 1.8,
  core = true,
  label = "Loading",
  className,
}: LoaderOrbitDotsProps) {
  const dot = Math.max(3, size * 0.14);

  return (
    <span
      role="status"
      aria-label={label}
      className={cn("relative inline-block shrink-0 text-primary", className)}
      style={{ width: size, height: size }}
    >
      <style>{`
        @keyframes pp-loader-orbit-dots-spin { to { transform: rotate(360deg); } }
        @keyframes pp-loader-orbit-dots-core { 0%, 100% { transform: scale(0.7); opacity: 0.35; } 50% { transform: scale(1); opacity: 0.8; } }
        @media (prefers-reduced-motion: reduce) {
          .pp-loader-orbit-dots-arm { animation-duration: 6s !important; animation-timing-function: linear !important; }
        }
      `}</style>
      {Array.from({ length: dots }, (_, i) => (
        // Each arm is a full-size box that rotates; the dot sits at its top edge.
        <span
          key={i}
          className="pp-loader-orbit-dots-arm absolute inset-0"
          style={
            {
              animation: `pp-loader-orbit-dots-spin ${speed}s cubic-bezier(0.55, 0.1, 0.3, 0.9) ${-i * speed * 0.085}s infinite`,
            } as CSSProperties
          }
        >
          <span
            className="absolute top-0 left-1/2 -translate-x-1/2 rounded-full bg-current"
            style={{ width: dot, height: dot, opacity: 1 - (i / dots) * 0.75, scale: 1 - (i / dots) * 0.45 }}
          />
        </span>
      ))}
      {core && (
        <span
          className="absolute inset-[34%] rounded-full bg-current motion-reduce:animate-none"
          style={{ animation: `pp-loader-orbit-dots-core ${speed}s ease-in-out infinite` }}
        />
      )}
    </span>
  );
}

export default function LoaderOrbitDotsDemo() {
  return (
    <div className="grid w-full max-w-xl grid-cols-3 gap-4">
      {[
        { label: "Small", note: "inline", node: <LoaderOrbitDots size={28} dots={4} core={false} /> },
        { label: "Default", note: "panels", node: <LoaderOrbitDots /> },
        {
          label: "Large",
          note: "full page",
          node: <LoaderOrbitDots size={84} dots={7} speed={2.4} className="text-chart-3" />,
        },
      ].map((v) => (
        <div
          key={v.label}
          className="flex flex-col items-center rounded-2xl border border-border bg-card px-4 pt-8 pb-5 shadow-sm"
        >
          <div className="flex h-24 items-center justify-center">{v.node}</div>
          <p className="mt-4 text-sm font-medium text-card-foreground">{v.label}</p>
          <p className="text-xs text-muted-foreground">{v.note}</p>
        </div>
      ))}
      <button
        type="button"
        disabled
        className="col-span-3 inline-flex h-11 items-center justify-center gap-2.5 rounded-xl bg-primary text-sm font-medium text-primary-foreground opacity-90"
      >
        <LoaderOrbitDots size={18} dots={3} core={false} className="text-primary-foreground" label="Saving" />
        Saving changes
      </button>
    </div>
  );
}
```

## AI prompt

```text
Add the "Loader Orbit Dots" component from PixelPapa UI to this project.

What it is: Dots chase each other around a ring, bunching at the top and fanning out as they fall, around a softly pulsing core.
Category: Loaders. Depends on: no extra packages.

1. Install it with the shadcn CLI:
   npx shadcn@latest add "https://pixelpapa.com/r/loader-orbit-dots.json"
   (If the CLI is unavailable, fetch that URL, write files[0].content to components/pixelpapa/loader-orbit-dots.tsx and install the listed dependencies.)
2. Import it from "@/components/pixelpapa/loader-orbit-dots" and place it where I describe below.
3. Keep its animation timings and accessibility attributes. Replace the demo copy and data with content that fits my product, and use my existing theme tokens (bg-background, text-foreground, bg-primary, border-border) rather than hard-coded colours.

Where to use it: <describe the page and position here>
```

## More loaders

- [Loader Bars Equalizer](https://pixelpapa.com/components/loaders/loader-bars-equalizer.md): Rounded bars bounce at different tempos like an audio meter; grows from the baseline or mirrors around the centre line.
- [Loader Infinity Path](https://pixelpapa.com/components/loaders/loader-infinity-path.md) (Pro): A comet of stroke dashes laps an SVG infinity loop, its tail built from stacked dashes that thin out behind the head.
- [Loader Morphing Shape](https://pixelpapa.com/components/loaders/loader-morphing-shape.md): A single shape turns a quarter at a time while its corners melt between square, circle, leaf and diamond, trailed by a lagging ghost.
- [Loader Progress Ring Percent](https://pixelpapa.com/components/loaders/loader-progress-ring-percent.md) (Pro): A ring that springs to its value while the percentage counts along inside it, then swaps to a check mark when it reaches 100.
- [Loader Skeleton Card](https://pixelpapa.com/components/loaders/loader-skeleton-card.md) (Pro): Skeleton blocks for a card and a list, swept by one shared diagonal shimmer per surface so every block glints in sync.
- [Loader Stepped Progress](https://pixelpapa.com/components/loaders/loader-stepped-progress.md) (Pro): A multi-step progress track: connectors fill, the active step spins, finished steps pop a check, and the status line rolls to the next message.
