Skip to content

shadcn.io is an independent project built on shadcn/ui

Components

Free

Generated Image for Radix UI

Show an image a model made, from its base64, in a frame that shimmers while it generates. For React with Radix UI, sized by the ratio you asked for.

The image a model made, in a frame of its aspect ratio. While it generates, the frame shimmers and says so; when it fails, the frame says that instead.

Use it for the output of the AI SDK's generateImage, or of an image tool in a chat: it takes the image as base64 or bytes with its media type, the shape the SDK returns. A picture you already have a URL for is a plain img. In React, the frame keeps the ratio from the first frame, so the reply doesn't jump when the image lands.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Generated Image anatomy: root, placeholder, image, ratio

Usage

import { GeneratedImage } from "@/components/ui/generated-image";
<GeneratedImage
  alt="Soft waves of violet and cyan light"
  base64={image.base64}
  className="max-w-64"
  mediaType="image/webp"
/>

Demos

States

Three values of state: generating draws the placeholder with its shimmer, error turns the frame red and says it failed, and done shows the image. Leave state out and the frame follows the data, generating until base64 or uint8Array arrives. Children replace the placeholder's words, as the error frame does here. Reduced motion holds the shimmer still.

Preview width

Aspect Ratio

aspectRatio takes the ratio in the form generateImage takes it: 16:9, 1:1 (the default), 9:16. Pass the one you asked the model for, so the placeholder already has the image's shape. A class such as aspect-video works too. The width is yours to set, with a class on the frame.

Preview width

In a Chat

The image inside an agent's reply, built on Message: it generates for three seconds, then comes into focus. Regenerate runs it again. The focus only plays for an image the reader saw generating; one that was ready when the page loaded just shows.

Preview width

AI SDK

generateImage runs on the server. Send the image's base64 and mediaType back as JSON: the result is a class whose bytes don't survive JSON.stringify whole.

app/api/image/route.ts
import { openai } from "@ai-sdk/openai";
import { experimental_generateImage as generateImage } from "ai";

export async function POST(request: Request) {
  const { prompt } = await request.json();
  const { image } = await generateImage({
    model: openai.image("gpt-image-1"),
    prompt,
    size: "1536x1024",
  });

  return Response.json({ base64: image.base64, mediaType: image.mediaType });
}
<GeneratedImage
  alt={prompt}
  aspectRatio="3:2"
  base64={image?.base64}
  mediaType={image?.mediaType}
/>

In a chat, make image generation a tool. Its output goes back to the model on the next turn, so toModelOutput sends the model a line instead of the whole base64.

app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { experimental_generateImage as generateImage, tool } from "ai";
import { z } from "zod";

const tools = {
  generateImage: tool({
    description: "Draw an image from a prompt",
    inputSchema: z.object({ prompt: z.string() }),
    execute: async ({ prompt }) => {
      const { image } = await generateImage({
        model: openai.image("gpt-image-1"),
        prompt,
      });
      return { base64: image.base64, mediaType: image.mediaType };
    },
    toModelOutput: () => ({ type: "text", value: "The image is shown." }),
  }),
};
const { messages } = useChat();

const stateOf = (state: string) => {
  if (state === "output-available") {
    return "done";
  }
  return state === "output-error" ? "error" : "generating";
};

// In each message's parts:
if (part.type === "tool-generateImage") {
  return (
    <GeneratedImage
      alt={part.input?.prompt}
      base64={part.output?.base64}
      key={part.toolCallId}
      mediaType={part.output?.mediaType}
      state={stateOf(part.state)}
    >
      {part.state === "output-error" ? part.errorText : null}
    </GeneratedImage>
  );
}

The tool part's state maps to the frame's:

Tool part statestate
input-streaming, input-availablegenerating
output-availabledone
output-errorerror, with errorText as its children

Migrating from AI SDK Elements

AI SDK Elements' Image is GeneratedImage here: a bare Image export would shadow the browser's Image constructor. Run the command in Installation, then change the import and keep the code that uses it:

import { GeneratedImage as Image } from "@/components/ui/generated-image";

It takes the same props, so <Image {...image} alt="…" /> keeps working. What differs:

  • className lands on the frame, a div, and the img fills it. The frame is square until you pass aspectRatio or an aspect-* class, where Elements' img took the image's own shape.
  • uint8Array is used when there's no base64. Elements ignored it.
  • There is no fixed width and height of 400: the frame fills its parent's width, so set one with a class.
  • An image with no data yet shows the generating placeholder, where Elements drew a broken img. Remove the Shimmer you showed in its place.

FAQ

What image data does Generated Image take?

The shape the AI SDK's generateImage returns: base64, or the bytes as uint8Array, with mediaType such as image/png, the default. The frame turns them into a data URL, so PNG, JPEG, WebP and GIF all work. For an image already at a URL, use a plain img.

How do I show that an image is still generating in React?

Set state to generating, or leave it out and pass no data yet: the frame shows a placeholder whose light sweeps across and whose words shimmer, and sets aria-busy. When the data arrives, the image takes its place in the same frame. Children replace the words, for a language other than English.

How do I show that image generation failed?

Set state to error. The frame turns red with a broken-image icon and says the image couldn't be generated; put the tool's errorText, or your own sentence, in its children. An image whose data won't decode shows the same frame on its own.

How do I keep the layout from jumping when the image loads?

Pass aspectRatio with the ratio you asked the model for, such as 16:9: the placeholder has the image's shape before it exists. It's 1:1 by default. Set the width with a class on the frame, such as max-w-64.

Does Generated Image respect reduced motion?

Yes. The sweep and the shimmer stop, and the image appears without coming into focus. Otherwise the focus plays once, for an image the reader saw generating: one already there when the page loads just shows. It needs no animation library in React: the keyframes come with the install.

What should the alt text of a generated image say?

What the image shows. The prompt is a good start, and the tool's input has it, as the AI SDK demo does with part.input?.prompt. Without alt the image is treated as decoration and screen readers skip it.

Is this AI SDK Elements' Image component?

It does the same job under another name, so it doesn't shadow the browser's Image. Import it as Image to keep your code. It adds the generating and error frames and the aspect ratio, and className styles the frame rather than the img.

API Reference

GeneratedImage

<div> data-slot="generated-image"

PropType
alt?string

What the image shows, for screen readers: the prompt is a good start.

Default
""
aspectRatio?`${number}:${number}`

Its width to its height, as generateImage takes it (16:9). The frame keeps it while it generates, so nothing moves when the image lands.

Default
"1:1"
base64?string

The image, base64-encoded.

children?React.ReactNode

The placeholder's words, in place of "Generating image" or, in the error state, "The image couldn't be generated".

mediaType?string

The image's type, such as image/png.

Default
"image/png"
state?GeneratedImageState

generating draws a shimmering placeholder, error says it failed, done shows the image. Left out, it's generating until there's an image.

uint8Array?Uint8Array

The image's bytes: used when there's no base64.

className?string

Merged with the part's classes.

AttributeType
data-state?string

Mirrors state.