18 / 37 · Knowledge
Image generation
A dot grid holds the frame while the image resolves out of a blur.
1024 × 1024
Generating
Installation
npx shadcn@latest add "@assistant-ui/elements-image-generation"
Usage
import { ImageGeneration } from "@/components/elements/image-generation";
<ImageGeneration prompt="A calm blue abstract" generating={false} />Props
prompt*stringPrompt text shown under the image frame.
generating*booleanWhen true the frame holds a pulsing dot grid; otherwise the image resolves.
classNamestringExtra classes merged onto the root.
Source
image-generation.tsx"use client";
import { RefreshCwIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { ghostButton, mono, paper } from "./surfaces";
const DOTS = Array.from({ length: 64 }, (_, i) => i);
export function ImageGeneration({
prompt,
generating,
className,
}: {
prompt: string;
generating: boolean;
className?: string;
}) {
return (
<div className={cn("flex w-52 flex-col gap-2.5", className)}>
<div
className={cn(
paper,
"relative aspect-square w-full overflow-hidden rounded-2xl",
)}
>
<div
className="absolute inset-0 grid grid-cols-8 place-items-center p-6"
aria-hidden
>
{DOTS.map((dot) => {
const row = Math.floor(dot / 8);
const col = dot % 8;
return (
<span
key={dot}
className={cn(
"bg-foreground/20 size-1 rounded-full transition-opacity duration-500",
generating
? "animate-pulse motion-reduce:animate-none"
: "opacity-0",
)}
style={{ animationDelay: `${(row + col) * 90}ms` }}
/>
);
})}
</div>
<div
aria-hidden
className={cn(
"absolute inset-0 transition-[opacity,filter] duration-1000 ease-out motion-reduce:transition-none",
generating ? "opacity-0 blur-xl" : "blur-0 opacity-100",
)}
style={{
background:
"radial-gradient(120% 90% at 20% 100%, oklch(0.45 0.09 265) 0%, transparent 55%), radial-gradient(110% 80% at 85% 90%, oklch(0.62 0.1 300 / 0.8) 0%, transparent 60%), radial-gradient(130% 100% at 60% 0%, oklch(0.88 0.06 60) 0%, oklch(0.74 0.09 25 / 0.9) 45%, transparent 75%), linear-gradient(to top, oklch(0.35 0.06 275), oklch(0.82 0.07 50))",
}}
/>
<span
className={cn(
mono,
"absolute end-2.5 top-2.5 tabular-nums",
generating ? "text-foreground/35" : "text-white/70",
)}
>
1024 × 1024
</span>
</div>
<div className="flex items-center justify-between gap-2">
<p className="text-foreground/45 min-w-0 flex-1 truncate text-xs">
{generating ? (
<span className="relative">
<span>Generating</span>
<span
aria-hidden
className="shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none"
>
Generating
</span>
</span>
) : (
prompt
)}
</p>
<button
type="button"
aria-label="Regenerate image"
className={cn(
ghostButton,
"size-6 shrink-0",
generating && "pointer-events-none opacity-0",
)}
>
<RefreshCwIcon className="size-3" />
</button>
</div>
</div>
);
}