Artifact
The panel beside an AI chat that shows what the model made. Web pages, SVGs, documents and code stream in live, then switch to a sandboxed preview, with versions you can compare and restore, a resizable split and a bottom sheet on phones.
When a model makes something worth keeping, like a web page, a document or a file, it belongs beside the chat instead of inside it. Artifact Workspace puts your chat and a panel side by side. The panel opens on its own when an artifact starts streaming, slides in while the chat makes room, and can be resized, expanded to take the whole width or dragged closed. On phones it becomes a bottom sheet that only opens when you tap a card, so reading the chat is never interrupted.
While the model writes, the panel shows highlighted code that follows new lines, with a Jump to latest button if you scroll up. Web pages and SVGs switch to their preview when writing finishes, never mid-stream, and the preview keeps showing the last finished version until then. Documents render as Markdown while they stream. Previews run in a sandboxed iframe with no access to your site, swap in only after the new version has loaded so there is no white flash, and report runtime errors with a Fix it action you can send back to the model.
Small changes don’t rewrite the file. An update can send edits, each a find and replace applied in order to the last finished version, like Claude’s artifacts. The code stays on screen and scrolls to each edit, removed lines are struck through and fold away, new text types in with a green tint, and the rest of the file stays still. The card says Editing with the number of changes. An edit whose text isn’t found, or is found more than once, fails that version with a message saying which edit and why, and the last good version stays current. Use edits for local changes and the whole content when most of the file or its structure changes.
Every update is a new version. The header lists them with what changed, Show changes compares a version with the one before it line by line with the same diff view as Diff Review, and older versions can be restored without deleting anything. The added and removed counts beside the toggle and on the card are the rows the diff shows. A new version always comes to the front. Cards in the chat show the version their message made, so clicking an old card opens that version. getArtifactsFromMessages builds all of this from AI SDK tool calls, and the panel, code view and preview also work on their own.
Add the Pro registry to components.json
components.json Add your token
Create a token on your account page and put it in
.env.localasHEXTAUI_PRO_TOKEN.Add the block
pnpm dlx shadcn@latest add @hextaui-pro/artifact
With the AI SDK
getArtifactsFromMessages turns create_artifact and update_artifact tool calls from useChat into artifacts with versions. Put an ArtifactCard where each call appears, and restore by adding a finished update call with setMessages. update_artifact runs in the browser, so applyEdits can tell the model when an edit didn’t match and it can try again.
The tools on your server
Two tools are enough: one creates an artifact with a stable id, the other changes it. A change is either edits, find and replace pairs for small changes, or the whole content for a rewrite. Both stream as tool input, so the panel fills, or the edit types in place, while the model writes.
A panel on its own
Show a saved artifact without a chat, for example on a share page. The panel keeps its own version, tab and compare state.
Anatomy
The parts you compose, from the outside in.
| Part | Description |
|---|---|
ArtifactWorkspace | The layout: your chat as children, the panel beside it on wide screens and in a bottom sheet on narrow ones. It decides what is open, opens new artifacts and announces progress. |
ArtifactCard | The card in a message that opens an artifact, or one version of it, and shows when it is being written, failed or open. |
ArtifactPanel | The header with title, versions, tabs and actions, and the code, changes, preview or document below it. |
ArtifactCode, ArtifactPreview | The streaming code and diff view, and the sandboxed preview with its error card. |
getArtifactsFromMessages, applyEdits | Reads create and update tool calls from AI SDK messages and returns artifacts with their versions. applyEdits applies find and replace edits the same way, so you can check an edit before telling the model it worked. |
useArtifactWorkspace | Opens and closes artifacts from your own controls, for example a list of files in a sidebar. |
| Prop | Type | Default |
|---|---|---|
artifactsEvery artifact in the conversation, in order. Usually getArtifactsFromMessages(messages). | Artifact[] | – |
childrenThe chat, usually a ChatThread. | ReactNode | – |
openIdThe open artifact, when you control it. An id that isn't in artifacts counts as closed. | string | null | – |
defaultOpenIdThe artifact open at first, when uncontrolled. | string | null | null |
onOpenChangeCalled when an artifact opens or the panel closes. | (id: string | null) => void | – |
autoOpenOpen an artifact on wide screens when a version starts streaming. It opens once per version, never after you close it during that stream, and never moves focus. | boolean | true |
defaultPanelSizeThe panel’s share of the width, in percent, when it opens. The chat keeps at least 320px and the panel at least 360px. Dragging the divider wins for the rest of the session. | number | 70 |
onRestoreShows Restore on older versions. Add the old content as a new version; nothing is deleted. | (artifact, version) => void | – |
onFixShows Fix it when the preview throws. error has message and line. | (artifact, version, error) => void | – |
actionsExtra header controls, such as Publish or Share. | (artifact) => ReactNode | – |
| Prop | Type | Default |
|---|---|---|
artifactIdThe artifact to open. Renders nothing if it doesn't exist. | string | – |
versionIdThe version this card stands for, usually the tool call id. Clicking opens that version, and the card says which version it is. | string | – |
ArtifactPanel
Rendered for you inside ArtifactWorkspace. Use it directly to show an artifact without a chat.
| Prop | Type | Default |
|---|---|---|
artifactWhat to show. | Artifact | – |
versionIdThe version shown, when you control it. null follows the latest version. | string | null | – |
onVersionChangeCalled when someone picks a version, with null for the latest. | (versionId: string | null) => void | – |
onCloseShows the close button and closes on Escape. | () => void | – |
fullscreen, onFullscreenChangeShows Expand and Show chat, and leaves expanded mode on Escape. | boolean, (fullscreen: boolean) => void | – |
onRestore, onFix, actionsThe same as on the workspace. | see ArtifactWorkspace | – |
Artifact
The data the components read.
| Prop | Type | Default |
|---|---|---|
id, titleA stable id and the title shown in the header and card. | string | – |
kindhtml and svg get a sandboxed preview, markdown renders as a document, code shows code only. | "html" | "svg" | "markdown" | "code" | – |
language, filenameHighlighting and the download name for code. | string | – |
versionsOldest first. status is streaming, complete, stopped or error; note says what changed. edits lists the find and replace pairs a targeted update applied; content is always the full result. | { id, content, status?, error?, note?, edits?, createdAt?, messageId? }[] | – |
| Prop | Type | Default |
|---|---|---|
messagesuseChat messages. Tool calls named create_artifact and update_artifact, or their camelCase forms, become versions. | UIMessage[] | – |
options.streamingWhether the last message is still arriving. Without it, unfinished calls count as stopped. | boolean | false |
options.toolsYour own tool names. Create reads id, title, kind, language, filename, description and content. Update reads id, description and either content or edits. | { create: string[]; update: string[] } | – |
update inputcontent rewrites the artifact. edits apply in order to the last finished version; each find must match exactly once, or the version fails with error saying which edit and why. If both are sent, content wins. | { content: string } | { edits: { find: string; replace: string }[] } | – |
| Key | Action |
|---|---|
| Enter | On a card, opens its artifact and moves focus to the panel title. On an open card, closes it. |
| Esc | Leaves expanded mode, then closes the panel and returns focus to the card. |
| ←→ | Resize the split while the divider has focus. Past the smallest size, the panel closes or the chat hides. |
| ←→ | Switch between Code and Preview while a tab has focus. |
| Tab | Moves through the header, the code or document area, which scrolls with the arrow keys, and the preview. |
- The panel is a labelled region with a real heading. Opening it from a card moves focus to that heading, and closing returns focus to the card. Opening on its own while the model writes never moves focus, so typing in the composer is never interrupted.
- Cards are buttons with aria-pressed and aria-controls, and say whether the artifact is being written, failed or stopped. A polite status announces when writing starts and when a version is ready, instead of reading every line.
- The preview iframe is titled after the artifact. Runtime errors appear as an alert with the message and line, and so does an edit that couldn’t be applied. Changes are announced as added and removed, not only shown in color.
- Every icon button has a label and a tooltip. Opening, closing and expanding slide with the chat reflowing smoothly, and with reduced motion the layout changes at once with a short fade. Edits fade in whole instead of typing.
Code
12 files, added to components/blocks/artifact.