# Image generation
URL: /elements/image-generation

A dot grid holds the frame while the image resolves out of a blur.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

Image generation holds the frame for an image that has not arrived yet: a pulsing dot grid over a blurred gradient while generating, settling into a sharp gradient once done. With a runtime it tracks a tool call's status; standalone you drive `prompt` and `generating` yourself.

## Getting started

**With a runtime:**

An image tool renders through that tool's own toolkit entry: the model's prompt arrives as `args`, and the call's lifecycle as `status`.

1. ### Define the image toolkit

   ```
   "use generative";

   import { defineToolkit, externalTool } from "@assistant-ui/react";
   import { ImageGeneration } from "@/components/assistant-ui/elements/image-generation";

   export default defineToolkit({
     generate_image: {
       execute: externalTool(),
       render: ({ args, status }) => (
         <ImageGeneration
           prompt={args.prompt ?? ""}
           generating={status.type === "running"}
         />
       ),
     },
   });
   ```

   `execute: externalTool()` marks a tool a backend route or image provider runs, not the browser: the compiler drops it from the client bundle and keeps only `render`.

2. ### Register the toolkit

   ```
   "use client";

   import { AssistantRuntimeProvider, AuiConfig, Tools } from "@assistant-ui/react";
   import { useChatRuntime } from "@assistant-ui/ai-sdk";
   import { Thread } from "@/components/assistant-ui/elements/thread.aui";
   import toolkit from "./toolkit";

   export function App() {
     const runtime = useChatRuntime();
     const config = AuiConfig({ tools: Tools({ toolkit }) });

     return (
       <AssistantRuntimeProvider runtime={runtime} config={config}>
         <Thread />
       </AssistantRuntimeProvider>
     );
   }
   ```

   Every `generate_image` tool call in the thread now renders as this element, wherever `MessagePrimitive.Parts` places it in the assistant's reply.

**Standalone (no runtime):**

Standalone, `ImageGeneration` is fully controlled: you hold the prompt and whether generation is in flight.

1. ### Hold the generation state

   ```
   "use client";

   import { useState } from "react";
   import { ImageGeneration } from "@/components/assistant-ui/elements/image-generation";

   export function Generation() {
     const [generating, setGenerating] = useState(true);

     return (
       <ImageGeneration
         prompt="A calm mountain lake at dawn"
         generating={generating}
       />
     );
   }
   ```

2. ### Flip it off once generation finishes

   `ImageGeneration` never receives the resulting URL, so hold it separately and swap in a real image renderer once it arrives:

   ```
   const [imageUrl, setImageUrl] = useState<string | null>(null);

   useEffect(() => {
     generateImage(prompt).then((url) => {
       setGenerating(false);
       setImageUrl(url);
     });
   }, [prompt]);

   return imageUrl ? <img src={imageUrl} alt={prompt} /> : (
     <ImageGeneration prompt={prompt} generating={generating} />
   );
   ```

## Anatomy

```
<div data-slot="image-generation">
  <div>
    {/* the frame: an 8x8 pulsing dot grid, over a fixed decorative gradient */}
    <span>{/* "1024 × 1024" */}</span>
  </div>
  <div>
    <p>{/* prompt, or a shimmering "Generating" label while generating */}</p>
    <button aria-label="Regenerate image" />
  </div>
</div>
```

The frame never renders an actual generated image: the gradient behind the dot grid is a fixed decorative graphic in the source, present in both states and only changing its blur and opacity as `generating` flips. Nothing in this element accepts an image URL. Once a tool call resolves with a real one, hand it to a renderer that does; see the [Image](/elements/image) element. The dot grid's 64 dots pulse with a staggered delay while `generating` is true and fade to fully transparent once it is false. The regenerate button fades out and stops receiving pointer events while generating, but has no `onClick` in the source: wire one by editing the installed file directly, the way you would any other owned-source element.

## Examples

### Wiring the regenerate button

The button exists in the DOM but the source has no handler for it. Add one where you install the element:

```
<button
  type="button"
  aria-label="Regenerate image"
  onClick={() => regenerate(prompt)}
  className={cn(ghostButton, "size-6 shrink-0", generating && "pointer-events-none opacity-0")}
>
  <RefreshCwIcon className="size-3" />
</button>
```

### Restyle the frame

Both lanes take `className` on the root. The "1024 × 1024" label and the prompt row use `mono` and `ShimmerLabel` from `surfaces.tsx`.

```
<ImageGeneration className="w-64" /* ... */ />
```

## API reference

**With a runtime:**

### Tool render props

| Prop          | Type                                                           | Description                                                                                |
| ------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `args`        | `{ prompt?: string }`                                          | Model-supplied arguments. Partial while streaming.                                         |
| `status.type` | `"running" \| "complete" \| "incomplete" \| "requires-action"` | Lifecycle of the call. `"running"` is the only state that should show the generating dots. |

See [Tool UI](/docs/tools/tool-ui) for the full tool-call part shape.

**Standalone (no runtime):**

### ImageGeneration

| Prop         | Type      | Default  | Description                                                                                   |
| ------------ | --------- | -------- | --------------------------------------------------------------------------------------------- |
| `prompt`     | `string`  | required | Shown below the frame once generation finishes.                                               |
| `generating` | `boolean` | required | Shows the pulsing dot grid, the blurred gradient, and the shimmering prompt label while true. |
| `className`  | `string`  |          | Merged onto the root.                                                                         |

All other `div` props are forwarded to the root.