Components
Right-aligned user chat bubble. Renders text, image previews, and non-image file attachments from a UIMessage payload. Self-contained port from 21st.dev Agent Elements.
npx shadcn@latest add https://21st.dev/r/21stdev/user-messageLoading preview...
import { UserMessage } from "@/components/ui/user-message";
const screenshotSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#1e293b"/>
<stop offset="1" stop-color="#0f172a"/>
</linearGradient>
</defs>
<rect width="320" height="200" fill="url(#bg)"/>
<rect x="0" y="0" width="320" height="28" fill="#020617"/>
<circle cx="14" cy="14" r="5" fill="#ef4444"/>
<circle cx="32" cy="14" r="5" fill="#f59e0b"/>
<circle cx="50" cy="14" r="5" fill="#22c55e"/>
<rect x="20" y="44" width="180" height="10" rx="2" fill="#cbd5e1"/>
<rect x="20" y="64" width="120" height="10" rx="2" fill="#94a3b8"/>
<rect x="20" y="92" width="280" height="60" rx="6" fill="#1e3a8a"/>
<rect x="32" y="104" width="100" height="8" rx="2" fill="#93c5fd"/>
<rect x="32" y="120" width="160" height="8" rx="2" fill="#dbeafe"/>
<rect x="32" y="136" width="80" height="8" rx="2" fill="#dbeafe"/>
<rect x="20" y="168" width="60" height="20" rx="4" fill="#22c55e"/>
</svg>`;
const screenshotUrl = `data:image/svg+xml;utf8,${encodeURIComponent(screenshotSvg)}`;
export default function Demo() {
return (
<div className="flex items-center justify-center w-full min-h-screen bg-background p-8 overflow-hidden">
<div className="w-full max-w-md">
<UserMessage
message={{
id: "msg-2",
parts: [
{
type: "file",
mimeType: "image/svg+xml",
url: screenshotUrl,
},
{ type: "text", text: "Here is the screenshot." },
],
}}
/>
</div>
</div>
);
}
Loading preview...