Skip to content
Kura UI

Artifact Card

beta

Card for something an agent made — document, code, web page, image, sheet or diagram — with generating/updating progress, versions and open-in-panel.

@kura-ui/react/artifact-cardCopy page for agents.mdregistry.json

Compact card

minimal
Markdown · 1,240 words
Q3 launch plan ready

Written live, then edited

Preview variant generating with progress and statusText, then ready with versions, Copy/Download actions and an edit that creates v2.

full
Outlining…0%
Generating Pricing page rewrite
Click the card to open it

Every status

Generating (indeterminate), updating with progress and versions, ready, failed with Retry.

state
Drafting email 2 of 4…
Generating Onboarding email sequence
Applying 3 edits…64%
Version v2 of 2
Updating use-session.ts
CSV · 12 columns · 4,218 rows
Churn by cohort ready
Stopped: the response hit the output limit
Waitlist landing page failed

Every kind

Document, code, web page, image, spreadsheet and diagram.

variant
Markdown · 860 words
Board update — October ready
TypeScript · 94 lines
rate-limiter.ts ready
React · interactive
Pricing page ready
PNG · 1600 × 900
Hero illustration ready
CSV · 1,204 rows
Q3 pipeline ready
Mermaid · 9 nodes
Auth flow ready

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.

composition
Rate limit the chat route to 20 requests per 10 seconds.
Kura

Here's a sliding-window limiter keyed by user. Call guard() at the top of the route; it throws a 429 with Retry-After when someone is over.

TypeScript · 21 lines
rate-limiter.ts ready

Installation

npx shadcn@latest add https://kuro.abstergo.fyi/r/artifact-card.json

Copies 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

PropTypeDefaultDescription
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.
metaReact.ReactNode—Secondary line when ready, e.g. "Markdown · 1,240 words" or "TypeScript · 94 lines". Default: the kind label.
statusTextReact.ReactNode—Live line while busy, e.g. "Writing section 3 of 5…". Default "Generating…" / "Updating…". Not announced on every change.
errorReact.ReactNode—Message for status="error". Default "Couldn't finish".
progressnumber—Completion 0–1 while busy: a determinate bar plus a mono percentage. Omit for an indeterminate sweep.
iconReact.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.
previewReact.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.
versionsnumber1Number of versions. Above 1 shows a ‹ v2/3 › switcher (disabled while busy).
versionnumber—Shown version, 1-based (controlled).
defaultVersionnumber—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.
hrefstring—Open by navigating instead: the title becomes a link stretched over the card.
selectedbooleanfalseThis 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".
actionsReact.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

  1. ArtifactCardRoot. `data-kind`, `data-status`, `data-variant`, `data-selected`; `aria-busy` while generating/updating.data-slot="artifact-card"
  2. PreviewThumbnail area (variant="preview" only), aria-hidden and inert.data-slot="artifact-card-preview"
  3. HeaderRow with icon, title, meta, versions and actions.data-slot="artifact-card-header"
  4. Icon36px kind tile; jade while busy, danger on error.data-slot="artifact-card-icon"
  5. 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"
  6. MetaSecond line: meta, shimmering statusText + percentage, or the error.data-slot="artifact-card-meta"
  7. Versions‹ v2/3 › switcher, read as “Version 2 of 3”.data-slot="artifact-card-versions"
  8. ActionsRetry (on error) and your `actions`.data-slot="artifact-card-actions"
  9. 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.
KeysAction
TabOpen control, then the version arrows, Retry and your actions
Enter / SpaceOpen 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.