Message
A comprehensive suite of components for displaying chat messages, including message rendering, branching, actions, and markdown responses.
Demo
Full-featured example with attachments, branching, and actions.
Svelte 5 Runes Guide
Svelte 5 introduces runes - a powerful new reactivity system using special $ prefixed functions. Here's what you need to know:$
Core Runes
Creates reactive state in your components:
<script lang="ts"> let count = $state(0);</script><button onclick={() => count++}> Count: {count}</button>Computes values that automatically update when dependencies change:
<script lang="ts"> let count = $state(0); let doubled = $derived(count * 2); let message = $derived.by(() => { return count > 10 ? 'High!' : 'Low'; });</script>When to Use Runes
- ✅ Component state - Use
$statefor reactive variables$ - ✅ Computed values - Use
$derivedfor values based on state$ - ✅ Side effects - Use
$effectwhen needed (but preferwatch()from runed)$ - ✅ Props - Use
$props()to declare component props$
Key Benefits
- Explicit reactivity - Clear what's reactive vs static
- Works everywhere - Runes work in .svelte.ts files too
- Better TypeScript - Improved type inference
Would you like to explore $effect, $props, or $bindable?$
Basic Usage
Simple user and assistant messages with markdown content.
Svelte 5 Runes System
Svelte 5 introduces runes - special compiler directives that make reactivity explicit. Here are the core runes:
Core Runes
| Rune | Purpose |
|---|---|
$state $ | Create reactive state |
$derived $ | Compute derived values |
$props $ | Declare component props |
$effect $ | Handle side effects |
Quick Example
<script lang="ts"> let count = $state(0); let doubled = $derived(count * 2);</script><button onclick={() => count++}> Count: {count}, Doubled: {doubled}</button>The key benefit is explicit reactivity - you always know what's reactive vs static.
Would you like to dive deeper into any specific rune?
Installation
If you prefer using jsrepo, please install via the command below:
Features
- Displays messages from both user and AI assistant with distinct styling and automatic alignment
- Minimalist flat design with user messages in secondary background and assistant messages full-width
- Response branching with navigation controls to switch between multiple AI versions
- File attachments display with file type icons, names, and sizes
- Action buttons with tooltips for common actions like copy, retry, like, dislike
- Built-in markdown rendering with syntax highlighting via svelte-streamdown
- Automatic dark/light theme support via mode-watcher
- Svelte 5 runes with class-based context for state management
Component Structure
Visual representation of how components are composed together:
With Attachments
Messages with file attachments like images and documents.
I've analyzed your files. Here's what I found:
File Analysis
📷 svelte-5-runes-demo.jpg
A visual demonstration of the new runes syntax with code examples showing $state and $derived patterns.
📄 component-architecture.pdf
Key Sections:
- Component hierarchy diagram
- State management patterns
- Props flow documentation
📝 notes.txt
Contains quick reference notes for the implementation.
Recommendations
- Consider using
$propsfor better type safety$ - The component structure follows best practices
- State is properly encapsulated
Would you like me to explain any specific part in more detail?
With Branching
Navigate between multiple AI response versions using branch selector.
Option 1: Local State with $state$
The simplest approach for component-local state:
<script> let count = $state(0);</script><button onclick={() => count++}> Clicks: {count}</button>✅ Best for: Simple counters, toggles, form inputs
With Actions
Action buttons for copy, regenerate, like, and dislike.
Svelte 5 Component Example
Here's a reactive greeting component using runes:
<script lang="ts"> let name = $state('World'); let greeting = $derived('Hello, ' + name + '!');</script><div class="card"> <h1>{greeting}</h1> <input bind:value={name} placeholder="Enter name" /></div>Key Concepts
| Rune | Usage in Example |
|---|---|
$state $ | Reactive name variable |
$derived $ | Auto-updating greeting |
This pattern is perfect for form inputs and dynamic content!
Usage with AI SDK
Integrate with Vercel AI SDK v5's Chat class for real-time streaming responses.
Add the following component to your frontend:
Add the following route to your backend:
Props
Message
| Prop | Type | Default | Description |
|---|---|---|---|
from | 'user' | 'assistant' | - | The role of the message sender |
children | Snippet | - | Child components (MessageContent, MessageActions, etc.) |
class | string | - | Additional CSS classes |
MessageContent
| Prop | Type | Default | Description |
|---|---|---|---|
children | Snippet | - | Content to render (typically MessageResponse) |
class | string | - | Additional CSS classes |
MessageResponse
| Prop | Type | Default | Description |
|---|---|---|---|
content | string | - | Markdown content to render with syntax highlighting |
class | string | - | Additional CSS classes |
MessageActions
| Prop | Type | Default | Description |
|---|---|---|---|
children | Snippet | - | MessageAction components |
class | string | - | Additional CSS classes |
MessageAction
| Prop | Type | Default | Description |
|---|---|---|---|
tooltip | string | - | Tooltip text shown on hover |
label | string | - | Accessible label for the action |
onclick | () => void | - | Click handler function |
MessageBranch
| Prop | Type | Default | Description |
|---|---|---|---|
defaultBranch | number | 0 | Initial branch index to display |
children | Snippet | - | MessageBranchContent and MessageBranchSelector |
MessageBranchContent
| Prop | Type | Default | Description |
|---|---|---|---|
content | T[] | - | Array of content items to render |
renderItem | Snippet<[T, number]> | - | Snippet to render each content item |
MessageAttachment
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | File name to display |
type | string | - | MIME type (e.g., 'image/png', 'application/pdf') |
size | number | - | File size in bytes |
onRemove | () => void | - | Handler called when remove button is clicked |