GitHub

Pulsating dots

Dots with scale and opacity motion for a lively wait state.

PulsatingDots uses soft scale and opacity changes to show ongoing work without implying a direction. It is a good fit for queues, imports, background processing, and compact UI surfaces where the state should feel alive but not urgent.

Loading
import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";

export function PulsatingDotsDemo() {

Installation

pnpm dlx shadcn@latest add @loading-ui/pulsating-dots

Usage

import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";
<PulsatingDots className="w-16" />

Customization

PulsatingDots is a flex row where each marker grows evenly inside the available width. Color flows through currentColor, while dots and duration let you tune the rhythm without changing the surrounding layout.

Size

Control the row width; the dots divide that space evenly and keep their square shape.

LoadingLoadingLoading
import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";

export function PulsatingDotsSize() {

Color

Use inherited foreground in product surfaces, or apply text color utilities when the loader needs a stronger status tone.

LoadingLoadingLoading
import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";

export function PulsatingDotsColor() {

Duration

duration is passed to Motion as seconds. Short cycles feel lively; longer cycles feel calmer.

LoadingLoadingLoading
import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";

export function PulsatingDotsDuration() {

Dot Count

Three dots are the default, but wider queues can use four or five markers for a denser pulse.

LoadingLoadingLoading
import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";

export function PulsatingDotsCount() {

Examples

These examples use the loader for soft background work: staging files, warming caches, queueing records, and small async hints.

Button

The row is narrow enough for button labels while still showing the full pulse.

import { Button } from "@/components/ui/button";
import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";

Badge

Badges work well for queue and retry states where the label still carries the meaning.

LoadingActiveLoadingQueuedLoadingRetrying
import { Badge } from "@/components/ui/badge";
import { PulsatingDots } from "@/components/loading-ui/pulsating-dots";

Input Group

Input groups keep the feedback local to the control that is still resolving.

LoadingLoading
LoadingReading files
import {
  InputGroup,
  InputGroupAddon,

Sheet

Bottom sheets give the pulse enough breathing room for import or batch progress without turning it into a full-page loading state.

import { Button } from "@/components/ui/button";
import {
  Sheet,

Popover

Popovers are useful for tiny background tasks where a concise hint is enough.

import { Button } from "@/components/ui/button";
import {
  Popover,