Skip to content
WorkspaceArtifact panelMenu

Artifact panel

The side panel for artifacts: versions, Code and Preview, copy, download and full screen. Resizable on desktop, a bottom sheet on phones.

An agent writes a React component, then revises it. The panel follows each version.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/artifact-panel.json

Usage

tsx
import { ArtifactLayout, ArtifactPanel } from "@/components/ui/chatcn/artifact-panel"
import { JsxPreview } from "@/components/ui/chatcn/jsx-preview"

<ArtifactLayout
  open={open}
  onOpenChange={setOpen}
  panel={
    <ArtifactPanel
      title="Usage meter"
      label="React component"
      language="tsx"
      content={code}
      streaming={streaming}
      versions={versions}
      version={versionId}
      onVersionChange={setVersionId}
      onClose={() => setOpen(false)}
      preview={({ content, streaming }) => <JsxPreview jsx={content} streaming={streaming} />}
    />
  }
>
  <Conversation />
</ArtifactLayout>

ArtifactLayout puts the panel beside the conversation at 45% by default, with a 360 px minimum, on top of Resizable. Below 768 px it becomes a bottom sheet. ArtifactPanel also works on its own inside any layout.

While streaming is true the panel shows code, follows the end and disables Download. When writing finishes it switches to the preview by itself, unless the reader picked a view.

The code view is a plain monochrome view with line numbers. To highlight it, pass renderCode={({ content, language, streaming }) => <CodeBlock ... />}.

Examples

HTML preview

HTML artifacts preview in an iframe with sandbox="allow-scripts allow-forms allow-popups allow-modals": scripts run, but on an opaque origin with no access to your app. While the HTML streams, the frame keeps the last complete version instead of reloading on every token.

An HTML artifact running in a sandboxed iframe.

Mobile sheet

On small screens the panel slides up as a sheet over 500 ms on the drawer curve. Drag the grabber down to dismiss; a quick flick works too.

Below 768 px the panel becomes a bottom sheet. Drag the grabber to dismiss.

Props

ArtifactPanel

PropTypeNotes
title, labelReactNodeHeader text
kind"code" | "document" | "html" | "image"HTML previews content in a sandbox, images show content as a URL
contentstringThe source
language, filenamestringDownload name and extension
streamingbooleanCaret, follow mode, Writing status
versions, version, onVersionChangeSee Artifact versions
view, defaultView, onViewChange"code" | "preview"Controlled or automatic
previewReactNode | (ctx) => ReactNodeCustom preview, for example JsxPreview
previewHtml, previewSrc, sandboxstringiframe preview
previewError, onFixErrorRuntime error bar with Fix error
renderCode(ctx) => ReactNodeReplace the code view
actionsReactNodeExtra header actions, use ArtifactPanelAction
onClose() => voidShows Close
fullscreen, defaultFullscreen, onFullscreenChangeboolean

ArtifactLayout

PropTypeNotes
open, onOpenChangePanel visibility
panelReactNodeUsually an ArtifactPanel
layout"auto" | "side" | "sheet"auto switches to the sheet below 768 px
defaultSize, minSize, maxSizenumber | "Npx"45%, 360 px and 75% by default
minContentSizenumber | "Npx"Smallest conversation width, 320 px
autoSaveIdstringPersist the split

Keyboard

KeyAction
← → in the view toggleSwitch between Code and Preview, without animation
Esc in the panelLeave full screen first, then close the panel
Esc in the sheetClose the sheet; focus returns to the card
Arrows on the dividerResize the panel

Accessibility

  • The panel is a labelled section. Code and Preview are tabs controlling the body.
  • Opening by itself during streaming never moves focus. The sheet is a modal dialog that traps focus and restores it.
  • The code is a focusable scroll region, so keyboard users can scroll long files.
  • Writing status sits in a polite live region next to the toggle.

Motion

  • Enter: 16 px from the side plus opacity, 280 ms on the default curve. Exit: 200 ms. Content inside never staggers.
  • The toggle pill slides 200 ms on the in-out curve with the pointer, and jumps when switched by keyboard.
  • The mobile sheet uses 500 ms on the drawer curve and tracks the finger 1:1 while dragged.
  • Reduced motion keeps the fades and drops every translate.