Open an artifact from a message
When the assistant generates a runnable result — a web page, an app, a component — your backend serves it at a URL and <kai-artifact> frames it live. You don’t have to show it the moment it’s ready: keep the conversation full-width, reference the generated file in the message, and let the reader open the preview when they want it. This is the v0 / Lovable / Replit model — generate, serve, then frame the running result in a side panel on demand.
<kai-artifact> frames a URL your backend serves (a preview sandbox, a dev server, object storage) — it doesn’t render files inline. Wire the reveal action to point the canvas at that URL.
How it works
Section titled “How it works”The reveal runs on a custom message action, not a special element. Give the assistant message an action descriptor and (optionally) attach the generated file so it reads as a real artifact:
chat.messages = [ { id: 'm2', role: 'assistant', content: "Here's a landing page for Starboard…", // the generated file, shown as a chip in the message attachments: [ { id: 'starboard-html', type: 'file', filename: 'index.html', title: 'Starboard landing page' }, ], // a custom action button that opens the canvas. Omit `icon` for a // label-only text button, or pass a curated icon name (e.g. 'link'). actions: [ { id: 'open-preview', label: 'Preview' }, 'copy', ], },];kai-chat fires kai-message-action with { messageId, action } when the button is clicked. Match your action id and reveal the artifact:
const artifact = document.getElementById('artifact');
chat.addEventListener('kai-message-action', (e) => { if (e.detail.action !== 'open-preview') return; artifact.files = projectFiles; // source for the Code tab artifact.src = '/artifacts/starboard/index.html'; // your hosted preview panel.hidden = false; // reveal the side panel});Lay the chat and the canvas side by side with <kai-resizable>, and keep the artifact panel hidden until it’s needed. Toggling the panel’s <kai-resizable-item hidden> lets the chat flex to full width when it’s closed:
<kai-resizable orientation="horizontal" style="display:block;height:100%"> <kai-resizable-item> <kai-chat id="chat"></kai-chat> </kai-resizable-item>
<!-- starts hidden; revealed by the open-preview action --> <kai-resizable-item id="panel" size="54%" min="360px" hidden> <kai-artifact id="artifact" iframe-title="Preview"></kai-artifact> </kai-resizable-item></kai-resizable>Add a close control in the panel that flips hidden back on, and the conversation returns to full width — the artifact’s state is preserved for the next open.
Why an action, not a clickable chip
Section titled “Why an action, not a clickable chip”Attachment chips render the file in the message, but kai-chat doesn’t emit a click event for them — they’re a visual reference. The clickable trigger is the action button, which fires kai-message-action. Pairing the two gives you the familiar artifact card: the file is named in the message, and a clear control opens it.
Next steps
Section titled “Next steps”- Artifacts canvas — the always-on version: a file tree and canvas docked beside the chat.
kai-artifactreference — the Preview/Code toggle, sandbox flags, and navigation toolbar.kai-messagereference — custom actions, attachments, and thekai-message-actionevent.- Resizable split — sizing and dragging the two panes.