Build a chat

The AI components are designed to stack into one anatomy: what the user said, what the assistant did, and what it answered. This guide composes a complete conversation screen from eight of them.

0. Install the parts

One command brings in everything this guide uses, dependencies included.

npx shadcn-svelte@latest add https://mizu-ui.com/r/v0.1.4/chat-bubble.json https://mizu-ui.com/r/v0.1.4/chat-input.json https://mizu-ui.com/r/v0.1.4/reasoning.json https://mizu-ui.com/r/v0.1.4/tool-call.json https://mizu-ui.com/r/v0.1.4/streaming-text.json https://mizu-ui.com/r/v0.1.4/sources.json https://mizu-ui.com/r/v0.1.4/message-actions.json https://mizu-ui.com/r/v0.1.4/prompt-suggestions.json

1. Messages

Conversations are two voices: the user on the accent, the assistant on the quiet fill. Bubbles animate in on arrival by default.

Find somewhere quiet for dinner Saturday.
On it. Checking your usual spots first.
<script lang="ts">
	import { ChatBubble } from '$lib/components/ui/chat-bubble';
</script>

<div class="flex flex-col gap-2.5">
	<ChatBubble role="user">Find somewhere quiet for dinner Saturday.</ChatBubble>
	<ChatBubble role="assistant">On it. Checking your usual spots first.</ChatBubble>
</div>

2. Show the work

Between the question and the answer lives the work. Reasoning folds the chain of thought into a quiet disclosure; Tool Call reports each thing the assistant actually did.

<script lang="ts">
	import { Reasoning } from '$lib/components/ui/reasoning';
	import { ToolCall } from '$lib/components/ui/tool-call';
</script>

<Reasoning summary="Thought for 4 seconds">
	Quiet matters more than fancy. Check the corner table at Verde first.
</Reasoning>

<ToolCall name="Checked Verde's availability" detail="Saturday, 7:00 PM" state="done">
	Two openings Saturday evening. The 7:00 slot holds the corner table.
</ToolCall>

3. The answer, streaming

Streaming Text types the reply in token by token. Ground it with Sources and finish with Message Actions so every answer can be copied, rated, or regenerated.

Booked for Saturday at seven. I picked the corner table you liked.
1 verde.rest2 maps
<script lang="ts">
	import { ChatBubble } from '$lib/components/ui/chat-bubble';
	import { StreamingText } from '$lib/components/ui/streaming-text';
	import { Sources } from '$lib/components/ui/sources';
	import { MessageActions } from '$lib/components/ui/message-actions';

	const reply = 'Booked for Saturday at seven. I picked the corner table you liked.';
</script>

<ChatBubble role="assistant" class="w-full max-w-[85%]">
	<StreamingText text={reply} speed={70} />
</ChatBubble>
<Sources items={[{ label: 'verde.rest', url: 'https://verde.rest' }]} />
<MessageActions text={reply} />

4. The composer

Prompt Suggestions invite the next message; Chat Input carries attach, voice, and send in one pill. Wire onSubmit to your model call and you have a working chat.

<script lang="ts">
	import { ChatInput } from '$lib/components/ui/chat-input';
	import { PromptSuggestions } from '$lib/components/ui/prompt-suggestions';

	let value = $state('');
</script>

<PromptSuggestions
	items={['Make it 8pm instead', 'Add it to my calendar']}
	onSelect={(p) => (value = p)}
/>
<ChatInput bind:value placeholder="Message Era..." onSubmit={(m) => send(m)} />

5. All together

Stacked in order inside a card, the pieces read as one screen: messages, the work, the streaming answer, the composer. The finished version ships as the Assistant chat block; grab the whole thing with one command or copy its source from the Blocks page.

npx shadcn-svelte@latest add https://mizu-ui.com/r/v0.1.4/assistant-chat.json