Elements

01 / 37 · Reasoning

Loading state

A pixel matrix that keeps time while the model has nothing to show yet.

Generating

Installation

npx shadcn@latest add "@assistant-ui/elements-loading-state"

Usage

import { GenerationLoader } from "@/components/elements/loading-state";

<GenerationLoader label="Generating" tick={24} />

Props

label*stringStatus copy shown under the pixel grid while the model has nothing else to show.
tick*numberAnimation clock that advances the pixel pattern and the elapsed seconds counter.
variant"dots" | "squares" | "rounded"Cell shape of the pixel matrix.
classNamestringExtra classes merged onto the root.

Source

loading-state.tsx
"use client";

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

export type GenerationLoaderVariant = "dots" | "squares" | "rounded";

export interface GenerationLoaderProps {
  label: string;
  tick: number;
  variant?: GenerationLoaderVariant;
  className?: string;
}

const CELL_SHAPES: Record<GenerationLoaderVariant, string> = {
  dots: "rounded-full",
  squares: "rounded-[1px]",
  rounded: "rounded-[3px]",
};

export function GenerationLoader({
  label,
  tick,
  variant = "dots",
  className,
}: GenerationLoaderProps) {
  const pixelOffset = Math.floor(tick / 3);

  return (
    <div className={cn("flex flex-col items-center gap-4", className)}>
      <div aria-hidden className="grid grid-cols-3 gap-1">
        {Array.from({ length: 9 }, (_, index) => {
          const active = (index * 2 + pixelOffset) % 9 < 3;

          return (
            <span
              key={index}
              className={cn(
                "bg-foreground size-2 transition-opacity duration-300 motion-reduce:transition-none",
                CELL_SHAPES[variant],
                active ? "opacity-90" : "opacity-15",
              )}
            />
          );
        })}
      </div>
      <span className="text-foreground/55 relative inline-block text-sm">
        <span>{label}</span>
        <span
          aria-hidden
          className="shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none"
        >
          {label}
        </span>
      </span>
    </div>
  );
}