Artifact Card
betaCard for something an agent made — document, code, web page, image, sheet or diagram — with generating/updating progress, versions and open-in-panel.
Compact card
Written live, then edited
Preview variant generating with progress and statusText, then ready with versions, Copy/Download actions and an edit that creates v2.
Every status
Generating (indeterminate), updating with progress and versions, ready, failed with Retry.
Every kind
Document, code, web page, image, spreadsheet and diagram.
In a message, opening a side panel
An assistant Message with a code artifact whose preview is a headerless CodeBlock; the card toggles a side panel with the full file.
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/artifact-card.jsonCopies the source into components/kura/artifact-card with its dependencies (kura-theme, icon-button, shimmer-text, use-controllable-state, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- An assistant message produced a standalone deliverable (a document, a code file, a web page, an image, a CSV, a diagram) that the user opens in a side panel or another page.
- Show an artifact while the agent is still writing it: status="generating" with a live statusText and optional progress, then flip to "ready".
- An artifact the user asked to change: status="updating" plus a ‹ v2/3 › version switcher (versions/version/onVersionChange).
- A list of everything the agent made in a session, e.g. a files/outputs sidebar (compact variant, selected marks the one that is open).
- A visual card with a thumbnail of the artifact under the title (variant="preview" with a `preview` node: a text excerpt, an <img>, a headerless CodeBlock).
Reach for something else when
- Code the user should read inline in the answer — use code-block directly.
- A file the user uploaded or attached to the prompt — use attachment.
- One tool invocation with arguments and a result (read_file, run_command) — use tool-call.
- A link to an external web source the answer relies on — use citation.
- A multi-step plan of work — use agent-timeline.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | string | — | The artifact's name, e.g. "Q3 launch plan" or "pricing-table.tsx". Truncates. |
| kind | "document" | "code" | "web" | "image" | "data" | "diagram" | "file" | "document" | What the agent made. Picks the icon, the spoken label (Document, Code, Web page, Image, Spreadsheet, Diagram, File) and the preview placeholder. |
| status | "generating" | "updating" | "ready" | "error" | "ready" | Lifecycle. generating = first write and updating = an edit (both: jade icon tile, shimmering statusText, progress bar, aria-busy); error = danger tile and message. |
| meta | React.ReactNode | — | Secondary line when ready, e.g. "Markdown · 1,240 words" or "TypeScript · 94 lines". Default: the kind label. |
| statusText | React.ReactNode | — | Live line while busy, e.g. "Writing section 3 of 5…". Default "Generating…" / "Updating…". Not announced on every change. |
| error | React.ReactNode | — | Message for status="error". Default "Couldn't finish". |
| progress | number | — | Completion 0–1 while busy: a determinate bar plus a mono percentage. Omit for an indeterminate sweep. |
| icon | React.ReactNode | — | Replaces the kind icon (lucide). |
| variant | "compact" | "preview" | "compact" | compact = one-row chip sized to its content, for message text and lists; preview = full-width card with a 144px thumbnail area above the title. |
| preview | React.ReactNode | — | Thumbnail content for variant="preview": a text excerpt, an <img className="size-full object-cover">, a headerless CodeBlock. It fades out at the bottom and is aria-hidden/inert. Default: a sketch of the kind that shimmers while busy. |
| versions | number | 1 | Number of versions. Above 1 shows a ‹ v2/3 › switcher (disabled while busy). |
| version | number | — | Shown version, 1-based (controlled). |
| defaultVersion | number | — | Initial version when uncontrolled. Default: the latest (`versions`). |
| onVersionChange | (version: number) => void | — | Called with the new 1-based version from the switcher. |
| onOpen | () => void | — | Open the artifact, e.g. into a side panel. The title becomes a button stretched over the whole card. |
| href | string | — | Open by navigating instead: the title becomes a link stretched over the card. |
| selected | boolean | false | This artifact is the one currently open (e.g. in the side panel): a stronger ring and aria-current on the open control. |
| onRetry | () => void | — | Shows a Retry icon button when status="error". |
| actions | React.ReactNode | — | Extra controls at the end of the row, e.g. Copy / Download IconButtons with size="sm". They sit above the stretched open target. |
All native attributes of the root element, className and ref pass through.
Anatomy
- ArtifactCardRoot. `data-kind`, `data-status`, `data-variant`, `data-selected`; `aria-busy` while generating/updating.
data-slot="artifact-card" - PreviewThumbnail area (variant="preview" only), aria-hidden and inert.
data-slot="artifact-card-preview" - HeaderRow with icon, title, meta, versions and actions.
data-slot="artifact-card-header" - Icon36px kind tile; jade while busy, danger on error.
data-slot="artifact-card-icon" - OpenThe title as a button (onOpen) or link (href) whose ::after covers the card. Without either, the title renders as `artifact-card-title`.
data-slot="artifact-card-open" - MetaSecond line: meta, shimmering statusText + percentage, or the error.
data-slot="artifact-card-meta" - Versions‹ v2/3 › switcher, read as “Version 2 of 3”.
data-slot="artifact-card-versions" - ActionsRetry (on error) and your `actions`.
data-slot="artifact-card-actions" - Progress2px role=progressbar at the bottom edge (of the preview, or of the compact card) while busy.
data-slot="artifact-card-progress"
Accessibility
- The open control is a real button or link named “<title>, <kind>[, generating | , updating | , failed]”; its ::after stretches over the card so the whole card is clickable while Versions and Actions stay separate tab stops.
- `selected` sets aria-current on the open control.
- The preview thumbnail is decorative (aria-hidden + inert): put anything a screen-reader user needs into title/meta.
- A polite status region announces lifecycle changes (“Generating Pricing page”, “Pricing page ready”, “… failed”); statusText and progress ticks are not announced.
- While busy the root has aria-busy and the bar is a named role=progressbar (with aria-valuenow when `progress` is given).
- Reduced motion: The shimmer and the indeterminate sweep stop (a still tinted track remains); the hover zoom of the preview, the press scale and the chevron nudge are removed. Colour and opacity changes stay.
| Keys | Action |
|---|---|
| Tab | Open control, then the version arrows, Retry and your actions |
| Enter / Space | Open the artifact (Enter follows the link for href) |
Notes for agents
- Keep the same card mounted from generating → ready so the transition (and the single announcement) is smooth; switch `status`, don't swap components.
- Drive `selected` from the id of the artifact open in your panel, so the card the user is looking at is marked.
- Use variant="compact" inside message text and lists (it sizes to content — give it a width like w-full or max-w-sm when stacking); use "preview" when there is room for a thumbnail.
- For images pass preview={<img src={url} alt="" className="size-full object-cover" />} — the thumbnail is decorative, the title carries the name.
- Pass `progress` only when you really know it (e.g. sections written / planned); otherwise leave it out for the indeterminate sweep.
- Map Anthropic/OpenAI canvas or artifact tool calls (create → generating, update/rewrite → updating with versions + 1) onto one card per artifact id.