The main component for rendering AI-generated content. Automatically detects and renders artifacts including code blocks, forms, social previews, images, videos, and more.
Import
import { ArtifactuseAgentMessage } from 'artifactuse/vue'
import { ArtifactuseAgentMessage } from 'artifactuse/vue2'
import { ArtifactuseAgentMessage } from 'artifactuse/react'
import { ArtifactuseAgentMessage } from 'artifactuse/svelte'
Usage
<template>
<ArtifactuseAgentMessage
:content="message.content"
:message-id="message.id"
:typing="isStreaming"
:is-last-message="isLastMessage"
@artifact-detected="onDetected"
@artifact-open="onOpen"
@form-submit="onFormSubmit"
@form-cancel="onFormCancel"
@form-button-click="onFormButtonClick"
@social-copy="onSocialCopy"
@media-open="onMediaOpen"
/>
</template>
<ArtifactuseAgentMessage
content={message.content}
messageId={message.id}
typing={isStreaming}
isLastMessage={isLastMessage}
onArtifactDetected={onDetected}
onArtifactOpen={onOpen}
onFormSubmit={onFormSubmit}
onFormCancel={onFormCancel}
onFormButtonClick={onFormButtonClick}
onSocialCopy={onSocialCopy}
onMediaOpen={onMediaOpen}
/>
<ArtifactuseAgentMessage
content={message.content}
messageId={message.id}
typing={isStreaming}
isLastMessage={isLastMessage}
on:artifact-detected={onDetected}
on:artifact-open={onOpen}
on:form-submit={onFormSubmit}
on:form-cancel={onFormCancel}
on:form-button-click={onFormButtonClick}
on:social-copy={onSocialCopy}
on:media-open={onMediaOpen}
/>
Props
| Prop | Type | Default | Description |
|---|
content | string | Required | Raw message content from AI (Markdown, code blocks, JSON artifacts) |
messageId | string | Required | Unique identifier for the message |
typing | boolean | false | Show typing indicator (use while streaming) |
inlineCards | boolean | true | Show clickable cards for panel artifacts inline |
isLastMessage | boolean | false | Whether this is the last message in the conversation. Keeps forms active after page reload. |
Events
| Event | Payload | Description |
|---|
artifact-detected | Artifact[] | Fired when artifacts are detected in content |
artifact-open | Artifact | Fired when user clicks an artifact card to open panel |
form-submit | { formId, action, values, timestamp } | Fired when an inline form is submitted |
form-cancel | { formId, action, buttonName, timestamp } | Fired when an inline form is cancelled |
form-button-click | { formId, action, buttonName, buttonLabel, values, timestamp } | Fired when a custom button is clicked |
social-copy | { platform, text } | Fired when social preview text is copied |
media-open | { type, src, alt, caption } | Fired when image/PDF is opened in lightbox viewer |
Event names use kebab-case in Vue (@form-submit) and camelCase in React (onFormSubmit). Svelte uses kebab-case with on: prefix (on:form-submit).
Artifact Rendering
The component automatically renders different artifact types:
| Artifact Type | Rendering |
|---|
code (HTML, React, Vue, etc.) | Clickable card → Opens in Panel |
form with display: "inline" | Inline form directly in message |
form with display: "panel" | Clickable card → Opens in Panel |
social | Inline social media preview |
| Images | Inline with lightbox on click |
| Videos | Inline embed (YouTube, Vimeo, etc.) |
| Other embeds | Inline (maps, documents, etc.) |
Inline forms automatically collapse after user interaction to prevent duplicate submissions and keep the chat clean.
| State | Description | Visual |
|---|
active | Interactive, user can fill and submit | Full form with fields |
submitted | User clicked submit or action button | Collapsed with ✓ checkmark |
cancelled | User clicked cancel | Collapsed with ✗ icon |
inactive | Historical form (page refresh) | Collapsed with — dash |
Behavior Rules
- Current session: Forms stay active until user interacts
- After submit/cancel/custom action: Form collapses immediately
- After page refresh:
- Last message forms stay active (via
isLastMessage prop)
- Older message forms collapse as inactive
- Reset action: Form stays active (doesn’t collapse)
Example with isLastMessage
<template>
<ArtifactuseAgentMessage
v-for="(msg, index) in messages"
:key="msg.id"
:content="msg.content"
:message-id="msg.id"
:is-last-message="index === messages.length - 1"
@form-submit="handleFormSubmit"
/>
</template>
Images and PDFs automatically open in a fullscreen lightbox viewer when clicked. The viewer supports:
- Zoom - Click image or zoom button to toggle zoom
- Download - Download the original file
- Keyboard - Press
Escape to close
- Click outside - Click overlay to close
<ArtifactuseAgentMessage
:content="content"
:message-id="id"
@media-open="({ type, src }) => console.log('Opened:', type, src)"
/>
Typing Indicator
Show a typing indicator while streaming AI responses:
<ArtifactuseAgentMessage
:content="partialContent"
:message-id="id"
:typing="isStreaming"
/>
The typing indicator displays animated loading bars that disappear when typing becomes false.
Example: Full Integration
<template>
<div class="chat">
<ArtifactuseAgentMessage
v-for="(msg, index) in messages"
:key="msg.id"
:content="msg.content"
:message-id="msg.id"
:typing="msg.isStreaming"
:is-last-message="index === messages.length - 1"
@form-submit="handleFormSubmit"
@form-cancel="handleFormCancel"
@form-button-click="handleFormButtonClick"
@social-copy="handleSocialCopy"
@media-open="handleMediaOpen"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ArtifactuseAgentMessage } from 'artifactuse/vue'
const messages = ref([])
function handleFormSubmit({ formId, values }) {
// Send form data back to AI
sendToAI(`User submitted form: ${JSON.stringify(values)}`)
}
function handleFormCancel({ formId }) {
// User cancelled the form
console.log('Form cancelled:', formId)
}
function handleFormButtonClick({ formId, action, buttonName, values }) {
// Handle custom button actions
console.log('Button clicked:', buttonName, action)
}
function handleSocialCopy({ platform, text }) {
// Track analytics
analytics.track('social_copy', { platform })
}
function handleMediaOpen({ type, src }) {
// Track media views
analytics.track('media_view', { type, src })
}
</script>
import { useState } from 'react'
import { ArtifactuseAgentMessage } from 'artifactuse/react'
function Chat() {
const [messages, setMessages] = useState([])
const handleFormSubmit = ({ formId, values }) => {
// Send form data back to AI
sendToAI(`User submitted form: ${JSON.stringify(values)}`)
}
const handleFormCancel = ({ formId }) => {
// User cancelled the form
console.log('Form cancelled:', formId)
}
const handleFormButtonClick = ({ formId, action, buttonName, values }) => {
// Handle custom button actions
console.log('Button clicked:', buttonName, action)
}
const handleSocialCopy = ({ platform, text }) => {
// Track analytics
analytics.track('social_copy', { platform })
}
const handleMediaOpen = ({ type, src }) => {
// Track media views
analytics.track('media_view', { type, src })
}
return (
<div className="chat">
{messages.map((msg, index) => (
<ArtifactuseAgentMessage
key={msg.id}
content={msg.content}
messageId={msg.id}
typing={msg.isStreaming}
isLastMessage={index === messages.length - 1}
onFormSubmit={handleFormSubmit}
onFormCancel={handleFormCancel}
onFormButtonClick={handleFormButtonClick}
onSocialCopy={handleSocialCopy}
onMediaOpen={handleMediaOpen}
/>
))}
</div>
)
}