01 / 122 · 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 type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
import { ShimmerLabel } from "./surfaces";
export type GenerationLoaderVariant = "dots" | "squares" | "rounded";
export interface GenerationLoaderProps extends Omit<
ComponentProps<"div">,
"children"
> {
label: string;
tick: number;
variant?: GenerationLoaderVariant;
}
const CELL_SHAPES: Record<GenerationLoaderVariant, string> = {
dots: "rounded-full",
squares: "rounded-[1px]",
rounded: "rounded-[3px]",
};
export function GenerationLoader({
label,
tick,
variant = "dots",
className,
...props
}: GenerationLoaderProps) {
const pixelOffset = Math.floor(tick / 3);
return (
<div
data-slot="generation-loader"
className={cn("flex flex-col items-center gap-4", className)}
{...props}
>
<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>
<ShimmerLabel className="text-foreground/55 relative inline-block text-sm">
{label}
</ShimmerLabel>
</div>
);
}