2ms
GitHub

Svelte Streamdown

npm version

A Svelte port of Streamdown by Vercel - an all in one markdown renderer, designed specifically for AI-powered streaming applications.

đŸ“Ļ Installation

bash
npm install svelte-streamdown# orpnpm add svelte-streamdown# oryarn add svelte-streamdown

🚀 Overview

Perfect for AI-powered applications that need to stream and render markdown content safely and beautifully, with support for incomplete markdown blocks, security hardening, and rich features like code highlighting, math expressions, and interactive diagrams.

✨ Main Features

🎨 Built-in Typography Styles

Beautiful, responsive typography with built-in Tailwind CSS classes for headings, lists, code blocks, and more. Comes with a complete default theme that works out of the box.

📝 GitHub Flavored Markdown

Full support for GitHub Flavored Markdown including:

  • Task lists
  • Tables
  • Strikethrough text

đŸ’ģ Interactive Code Blocks

  • Syntax highlighting powered by Shiki
  • Copy-to-clipboard functionality
  • Support any Shiki themes

đŸ”ĸ Mathematical Expressions

LaTeX math support through KaTeX:

  • Perfect rendering for scientific content
  • Inline math:
  • Block math:

đŸ§œâ€â™€ī¸ Mermaid Diagrams

  • Render Mermaid diagrams from code blocks
  • Incremental rendering during streaming content
  • Pan and Zoom
  • Full screen mode

Example:

Alert Support

important

Native support for Github style Alert

🔄 Streaming-Optimized

  • Incomplete Markdown Parsing: Handles unterminated blocks gracefully
  • Progressive Rendering: Perfect for streaming AI responses
  • Real-time Updates: Optimized for dynamic content

🔒 Security Hardening

  • Image Origin Control: Whitelist allowed image sources
  • Link Safety: Control link destinations
  • HTML Sanitization: Prevent XSS attacks

đŸŽ¯ Fully Customizable Components & Theming

  • Every component customizable with Svelte snippets
  • Granular theming system - customize every part of every component
  • Override default styling and behavior for any markdown element
  • Full control over rendering with type-safe props
  • Seamless integration with your design system

🔄 Differences from Original React Version

This Svelte port maintains feature parity with the original Streamdown while adapting to Svelte's patterns:

Aspect Original (React) Svelte Port
Framework React Svelte 5
Component API JSX Components Svelte Snippets
Styling Tailwind CSS Tailwind CSS (compatible)
Context React Context Svelte Context
Build System Vite/React Vite/SvelteKit
TypeScript Full TS support Full TS support

Tailwind CSS Setup

note

Streamdown comes with built-in Tailwind CSS classes for beautiful default styling. To ensure all styles are included in your build, add the following to your app.css or main CSS file:

css
@import 'tailwindcss';/* Add Streamdown styles to your Tailwind build */@source "../node_modules/svelte-streamdown/**/*";

This ensures that all Streamdown's default styling is included in your Tailwind build process.

Note: This setup is primarily necessary if you're using Tailwind CSS v4's new @source directive or if you have aggressive purging enabled in older versions. If you're using standard Tailwind CSS v3+ with default purging, Streamdown's styles should be automatically included when the component is imported and used in your application.

🚀 Quick Start

Basic Usage

svelte
<script> import { Streamdown } from 'svelte-streamdown'; let content = `# Hello WorldThis is a **bold** text and this is *italic*.\`\`\`javascriptconsole.log('Hello from Streamdown!');\`\`\``;</script><Streamdown {content} />

Advanced Usage with Custom Components

svelte
<script> import { Streamdown } from 'svelte-streamdown'; let content = `# Custom Components ExampleThis heading will use a custom component!`; // Custom heading component</script>{#snippet customH1({ children, props })} <h1 class="mb-4 text-4xl font-bold text-blue-600" {...props}> {children} </h1>{/snippet}<Streamdown {content} h1={customH1} />

Security Configuration

svelte
<script> import { Streamdown } from 'svelte-streamdown'; let markdown = `![Safe Image](https://trusted-domain.com/image.jpg)[Safe Link](https://trusted-domain.com/page)`;</script><Streamdown {content} allowedImagePrefixes={['https://trusted-domain.com']} allowedLinkPrefixes={['https://trusted-domain.com']}/>

📋 Props API

Prop Type Default Description
content string - Required. The markdown content to render
class string - CSS class names for the wrapper element
parseIncompleteMarkdown boolean true Parse and fix incomplete markdown syntax
defaultOrigin string - Default origin for relative URLs
allowedLinkPrefixes string[] ['*'] Allowed URL prefixes for links
allowedImagePrefixes string[] ['*'] Allowed URL prefixes for images
allowElement AllowElement | null - Custom element filtering function
allowedElements readonly string[] | null - Whitelist of allowed HTML elements
disallowedElements readonly string[] | null - Blacklist of disallowed HTML elements
skipHtml boolean - Skip HTML parsing entirely
unwrapDisallowed boolean - Unwrap instead of removing disallowed elements
urlTransform UrlTransform | null - Custom URL transformation function
theme Partial<Theme> - Custom theme overrides
baseTheme 'tailwind' | 'shadcn' 'tailwind' Base theme to use before applying overrides
mergeTheme boolean true Whether to merge theme with base theme
shikiTheme BundledTheme 'github-light' Code highlighting theme
mermaidConfig MermaidConfig - Mermaid diagram configuration
katexConfig KatexOptions | ((inline: boolean) => KatexOptions) - KaTeX math rendering options
remarkPlugins PluggableList - Additional remark plugins
rehypePlugins PluggableList - Additional rehype plugins
remarkRehypeOptions RemarkRehypeOptions - Remark-rehype conversion options
customElements Record<string, Snippet<[ElementProps]>> - Custom snippets for not handled nodes

Custom Component Props

Every single markdown element can be customized with Svelte snippets, giving you complete control over styling and behavior:

svelte
<script> import { Streamdown } from 'svelte-streamdown'; let content = `# Fully CustomizableThis heading uses a custom component with your design system!`;</script>{#snippet customH1({ children, ...props })} <h1 class="text-gradient mb-6 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-4xl font-bold text-transparent" {...props} > {@render children()} </h1>{/snippet}{#snippet customCode({ children, ...props })} <code class="rounded bg-gray-100 px-2 py-1 font-mono text-sm dark:bg-gray-800" {...props}> {@render children()} </code>{/snippet}{#snippet customBlockquote({ children, ...props })} <blockquote class="border-l-4 border-blue-500 pl-4 text-gray-600 italic dark:text-gray-300" {...props} > {@render children()} </blockquote>{/snippet}<Streamdown {content} h1={customH1} code={customCode} blockquote={customBlockquote} />

All Available Customizable Elements:

Text Elements: h1, h2, h3, h4, h5, h6, p, strong, em, del

Links & Media: a, img

Lists: ul, ol, li

Code: code, inlineCode, pre

Tables: table, thead, tbody, tr, th, td

Special Content: blockquote, hr, alert, mermaid, math, inlineMath

Note: The above elements are supported by Streamdown and should be customized using individual props or the theme system. Use customElements only for HTML elements not in this list (like div, span, section, article, etc.).

Each snippet receives { children, ...props } where props contains all element attributes and classes.

Using customElements Record

The customElements prop is specifically for HTML elements that are not handled by the library by default. For elements already supported by Streamdown (like h1, p, code, etc.), use individual props or the theme system instead.

svelte
<script> import { Streamdown } from 'svelte-streamdown'; let content = `# Custom Elements ExampleThis content contains HTML elements not handled by Streamdown by default:<div class="special">This is a custom div element</div><span class="highlight">This is a custom span element</span><section class="wrapper"> <article>This is a custom article inside a section</article></section>`; // Define custom components for unsupported HTML elements</script>{#snippet customDiv({ children, props, className, node })} <div class="rounded-lg border-2 border-blue-500 p-4 {className}" {...props}> {@render children()} </div>{/snippet}{#snippet customSpan({ children, props, className, node })} <span class="rounded bg-yellow-200 px-2 py-1 {className}" {...props}> {@render children()} </span>{/snippet}{#snippet customSection({ children, props, className, node })} <section class="my-8 rounded-xl bg-gray-50 p-6 {className}" {...props}> {@render children()} </section>{/snippet}{#snippet customArticle({ children, props, className, node })} <article class="prose max-w-none {className}" {...props}> {@render children()} </article>{/snippet}<Streamdown {content} customElements={{ div: customDiv, span: customSpan, section: customSection, article: customArticle }}/>

Benefits of customElements

  • Handle Unsupported Elements: Define components for HTML elements not built into Streamdown
  • Semantic HTML Support: Use elements like <section>, <article>, <aside>, <nav>, etc.
  • Fallback Support: Automatically handles unknown elements that would otherwise be ignored
  • Type Safety: Full TypeScript support with ElementProps interface

customElements vs Individual Props

Use the right approach for the right elements:

Individual Props (for supported Streamdown elements):

svelte
<Streamdown {content} h1={customH1} p={customP} code={customCode} />

CustomElements Record (for unsupported HTML elements):

svelte
<Streamdown {content} customElements={{ div: customDiv, span: customSpan, section: customSection }}/>

Note: For supported elements (h1, p, code, etc.), use individual props or the theme system. For unsupported elements (div, span, section, etc.), use customElements.

🎨 Advanced Theming System

Built-in Themes

Streamdown comes with two built-in themes:

  • Default Theme: The standard theme with gray-based colors
  • Shadcn Theme: A theme that uses shadcn/ui design tokens for seamless integration with shadcn-based projects

Beyond custom snippets, Streamdown provides a granular theming system that lets you customize every part of every component without writing custom snippets. You can use the built-in themes (default and shadcn) or create completely custom themes using the mergeTheme utility.

Theme Structure

Every component has multiple themeable parts. For example, the code component has:

typescript
code: { base: 'bg-gray-100 rounded p-2 font-mono text-sm', // Main code block container: 'my-4 w-full overflow-hidden rounded-xl border', // Wrapper container header: 'flex items-center justify-between bg-gray-100/80', // Header with language button: 'cursor-pointer p-1 text-gray-600 transition-all', // Copy button language: 'ml-1 font-mono lowercase', // Language label pre: 'overflow-x-auto font-mono p-0 bg-gray-100/40' // Pre element}

Using Custom Themes

svelte
<script> import { Streamdown } from 'svelte-streamdown'; let content = `# Custom Theme Example\`\`\`javascriptconsole.log('Beautiful code blocks!');\`\`\`> This blockquote is also themed| Header 1 | Header 2 ||----------|----------|| Cell 1 | Cell 2 |`; // Custom theme overrides let customTheme = { code: { container: 'my-6 rounded-2xl border-2 border-purple-200 shadow-lg', header: 'bg-purple-50 text-purple-700 font-medium', button: 'text-purple-600 hover:text-purple-800 hover:bg-purple-100' }, blockquote: { base: 'border-l-8 border-purple-400 bg-purple-50 p-4 italic text-purple-800' }, table: { base: 'border-purple-200 shadow-md', container: 'my-6 rounded-lg overflow-hidden' }, th: { base: 'bg-purple-100 px-6 py-3 text-purple-900 font-bold' }, td: { base: 'px-6 py-3 border-purple-100' } };</script><Streamdown {content} theme={customTheme} />

All Themeable Components

Each component supports multiple themeable parts:

Headings (h1-h6): base

Text Elements (p, strong, em, del): base

Lists (ul, ol, li): base

Links (a): base, blocked (for blocked/unsafe links)

Code (code): base, container, header, button, language, skeleton, pre

Inline Code (inlineCode): base

Images (img): container, base, downloadButton

Tables (table, thead, tbody, tr, th, td): base, container (table only)

Blockquotes (blockquote): base

Alerts (alert): base, title, icon, plus type-specific styles (note, tip, warning, caution, important)

Mermaid (mermaid): base, downloadButton

Math (math, inlineMath): base

Other (hr, sup, sub): base

Theme Merging

Themes are intelligently merged using Tailwind's class merging utility, so you only need to override the specific parts you want to customize while keeping the default styling for everything else.

đŸ› ī¸ Development

Setup

bash
# Clone the repositorygit clone <repository-url>cd svelte-streamdown# Install dependenciespnpm install# Start development serverpnpm dev# Run testspnpm test# Build for productionpnpm build

Building

bash
# Build the librarypnpm build# Preview the showcase apppnpm preview

🤝 Contributing

Contributions are welcome! This is a port of the original Streamdown project, so please:

  1. Check the original Streamdown repository for upstream changes
  2. Ensure compatibility with the original API
  3. Maintain feature parity where possible
  4. Add tests for new features if you want

📄 License

MIT

🙏 Acknowledgments

  • Original Streamdown: Vercel for creating the original React component
  • Svelte Community: For the amazing framework that made this port possible
  • All Contributors: For helping improve and maintain this project

Made with â¤ī¸ and 🤖