A high-performance virtual chat viewport for Svelte 5.
Purpose-built for LLM conversations, support chat, and any message-based UI.
This is a live SvelteVirtualChat component. Messages stream token-by-token while the viewport stays pinned to bottom — no jitter.
Every feature is a direct response to a real problem in chat UI development.
Viewport stays pinned to the newest message. Scroll away and it stops. Return and it resumes.
Height changes from token streaming are batched per frame. No jitter, no jumps, no workarounds.
Only visible messages exist in the DOM. 10,000 messages renders ~20 DOM nodes.
Load older messages at the top. Scroll anchor preservation keeps the user's position stable.
Uses message IDs for identity. Height caching, scroll-to-message, and keyed rendering built in.
Full generics, runes ($state, $derived, $effect), and snippets. Zero dependencies.
One component, one snippet, done. Pair with @humanspeak/svelte-markdown for rich LLM output.
<script lang="ts">
import SvelteVirtualChat from '@humanspeak/svelte-virtual-chat'
let messages = $state([...])
</script>
<SvelteVirtualChat
{messages}
getMessageId={(msg) => msg.id}
estimatedMessageHeight={72}
containerClass="h-[600px]"
viewportClass="h-full"
>
{#snippet renderMessage(message, index)}
<div class="p-4">{message.content}</div>
{/snippet}
</SvelteVirtualChat>pnpm add @humanspeak/svelte-virtual-chat