Skip to documentation content

Artifact Panel

Give something an agent made, such as a file or a document, a surface beside the conversation that can be opened again.

artifact-panel-demo

Overview

Some replies are not messages. A file, a document, or a page the agent wrote is something a person returns to, and it gets lost when it scrolls away inside a thread. Artifact Panel gives it a surface of its own, and Artifact Card leaves a mention of it in the message, so it can be opened again.

The panel is a frame: a header with the title, a status, your actions, and Close, over a body that scrolls. What goes in the body is yours, such as a Code View, a Markdown document, or a preview.

Neither component holds the artifact or decides when it shows. Your application keeps which artifact is open, renders the panel when one is, and places it in your layout.

Anatomy

ArtifactPanel
  • ArtifactPanelThe frame beside the conversation
    • HeaderThe title, a short description, the status, your actions, and Close
    • ErrorA line under the header when the artifact stopped early, with Try again
    • BodyYour content, in a region that scrolls
  • ArtifactCardThe mention of the artifact inside a message, which opens the panel

Installing artifact-panel brings Button along as a registry dependency.

Behavior

Status

status is streaming, complete, or error, and defaults to complete. Pass the same value to the panel and to the card.

  • streaming shows "Writing…" in the panel's header and on the card.
  • complete shows the description.
  • error shows a line under the header saying the artifact stopped early, and the same sentence on the card. What was written so far stays in the body.

Passing onRetry adds Try again to the error line. Requesting the artifact again and resetting status are your application's work.

Opening and closing

Artifact Card is a button. Give it an onClick that opens the panel, and pass isOpen so it can show which state it is in: "Open" when the panel is closed, "Showing" when it is open.

Passing onClose to the panel adds Close. A panel with no onClose has no Close button, for a layout where the panel is always present.

Layout

The panel fills its parent's width and height. Give the parent a height, or the body will grow with its content and never scroll.

Where the panel sits is your layout's decision. A common one is two columns on a wide screen, and the panel in place of the conversation on a narrow one, with Close leading back. The preview above is narrow, so it puts the panel under the message.

Content that reaches the edges

The body has padding, which suits a document. Set flush to remove it for content that should reach the panel's edges, such as code. A Code View with embedded drops its own frame and header, so the panel's header is the only one. An embedded Code View has no Copy button, so put your own in actions, as the preview does.

Focus

When a person opens the panel, set autoFocus, so the panel takes focus and a keyboard or screen reader user lands in what they asked for. When a reply opens the panel by itself, leave autoFocus off, so focus is not pulled out of the prompt the person is typing.

Closing the panel removes the focused button. In onClose, return focus to the card that opened it, as the preview does.

Accessibility

The panel is a region named by its title. "Writing…" is a status and the error line is an alert, so both are announced when they appear. The body can take focus, so it can be scrolled from the keyboard.

The card reports whether its panel is open through aria-expanded, and shows the same thing in words. Close and Try again are buttons with names.

The panel is not a dialog. It does not trap focus or close on Escape, because the conversation beside it stays usable. If your narrow layout covers the conversation with the panel, the conversation should be hidden from assistive technology while it is covered.

Every string comes from labels.

The preview has been checked with a keyboard and with axe. It has not been checked with a screen reader yet.

KeyAction
Enter or SpaceOpen the panel from its card, or activate Close or Try again
TabMove through the header's controls, then into the body
Arrow keysScroll the focused body

Installation

npx honestui@latest add https://ai.honestui.com/r/artifact-panel.json

Usage

import { ArtifactCard, ArtifactPanel } from "@/components/ai/artifact-panel"

// In the message that produced the artifact:
<ArtifactCard
  ref={cardRef}
  title={artifact.title}
  description={artifact.kind}
  status={artifact.status}
  isOpen={openArtifactId === artifact.id}
  onClick={() => setOpenArtifactId(artifact.id)}
/>

// Beside the conversation:
{openArtifact && (
  <ArtifactPanel
    title={openArtifact.title}
    description={openArtifact.kind}
    status={openArtifact.status}
    autoFocus
    onRetry={() => agent.rewrite(openArtifact.id)}
    onClose={() => {
      setOpenArtifactId(null)
      cardRef.current?.focus()
    }}
  >
    <Markdown>{openArtifact.text}</Markdown>
  </ArtifactPanel>
)}

Code, with your own actions

actions places controls in the header, before Close. For code, pair flush with an embedded Code View:

<ArtifactPanel
  title={artifact.title}
  flush
  actions={
    <Button variant="link" size="sm" onClick={() => copy(artifact.text)}>
      Copy
    </Button>
  }
>
  <CodeView embedded filename={artifact.title} code={artifact.text} />
</ArtifactPanel>

Examples

Stopped early

A draft that stopped before it finished. Try again stands in for a second attempt.

artifact-panel-error

API reference

ArtifactPanel

Accepts native section props beyond those listed. Its children are the body.

PropTypeDefaultDescription
titlestringRequiredNames the artifact.
descriptionstringNoneA short note under the title.
statusArtifactStatus"complete"Whether the artifact is being written, is finished, or stopped early.
actionsReactNodeNoneControls in the header.
onClose() => voidNoneShows Close.
onRetry() => voidNoneShows Try again when status is "error".
flushbooleanfalseRemoves the body's padding.
autoFocusbooleanfalseMoves focus to the panel when it mounts.
labelsPartial<ArtifactLabels>EnglishControl names and status text.

ArtifactCard

Accepts native button props beyond those listed, including onClick.

PropTypeDefaultDescription
titlestringRequiredThe same title as the panel it opens.
descriptionstringNoneA short note under the title.
statusArtifactStatus"complete"Replaces the description while the artifact is unfinished.
isOpenbooleanfalseWhether this artifact's panel is showing.
iconReactNodeA documentShown before the title.
labelsPartial<ArtifactLabels>EnglishControl names and status text.

Types

ExportShape
ArtifactStatus"streaming", "complete", or "error"
ArtifactLabelsopen, showing, close, streaming, error, retry