{"id":31189570,"url":"https://github.com/beynar/svelte-streamdown","last_synced_at":"2025-12-05T03:04:40.749Z","repository":{"id":313777576,"uuid":"1052087300","full_name":"beynar/svelte-streamdown","owner":"beynar","description":"Svelte port of Streamdown","archived":false,"fork":false,"pushed_at":"2025-09-24T14:46:33.000Z","size":478,"stargazers_count":18,"open_issues_count":1,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-24T16:38:49.190Z","etag":null,"topics":["ai","ai-sdk","katex","mardkown","mermaid","shiki","svelte"],"latest_commit_sha":null,"homepage":"https://svelte-streamdown.beynar.workers.dev/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/beynar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-07T11:32:02.000Z","updated_at":"2025-09-24T15:27:46.000Z","dependencies_parsed_at":"2025-09-24T16:25:44.828Z","dependency_job_id":"f4d77be7-a171-413a-8813-842994b3b317","html_url":"https://github.com/beynar/svelte-streamdown","commit_stats":null,"previous_names":["beynar/svelte-streamdown"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/beynar/svelte-streamdown","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beynar%2Fsvelte-streamdown","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beynar%2Fsvelte-streamdown/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beynar%2Fsvelte-streamdown/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beynar%2Fsvelte-streamdown/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beynar","download_url":"https://codeload.github.com/beynar/svelte-streamdown/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beynar%2Fsvelte-streamdown/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000701,"owners_count":26082805,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-sdk","katex","mardkown","mermaid","shiki","svelte"],"created_at":"2025-09-19T21:01:41.377Z","updated_at":"2025-12-05T03:04:40.743Z","avatar_url":"https://github.com/beynar.png","language":"TypeScript","funding_links":[],"categories":["UI Components","TypeScript"],"sub_categories":["Miscellaneous"],"readme":"# Svelte Streamdown\n\n![npm version](https://badge.fury.io/js/svelte-streamdown.svg)\n\nA **Svelte port** of [Streamdown](https://streamdown.ai/) by Vercel - an all in one markdown renderer, designed specifically for AI-powered streaming applications.\n\n## 📦 Installation\n\n```bash\nnpm install svelte-streamdown\n# or\npnpm add svelte-streamdown\n# or\nyarn add svelte-streamdown\n```\n\n## 🚀 Overview\n\nPerfect 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.\n\n## ✨ Main Features\n\n### 🔄 Streaming-Optimized\n\n- **Incomplete Markdown Parsing**: Handles unterminated blocks gracefully\n- **Progressive Rendering**: Perfect for streaming AI responses\n- **Real-time Updates**: Optimized for dynamic content\n- **Smooth Animations**: Animate tokens and blocks as they are streamed.\n\n### 🔒 Security Hardening\n\n- **Image Origin Control**: Whitelist allowed image sources\n- **Link Safety**: Control link destinations\n\n### 🎯 Fully Customizable Components \u0026 Theming\n\n- **Every component customizable** with Svelte snippets\n- **Granular theming system** - customize every part of every component\n- Override default styling and behavior for any markdown element\n- Full control over rendering with type-safe props\n- Seamless integration with your design system\n\n### 🎨 Built-in Typography Styles\n\nBeautiful, 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.\n\n### 📝 Extensive Markdown Features\n\nFull support for\n\n- Basic text marks: **bold**, _italic_, `code`, ~~Strikethrough~~\n- ~Subscript~ and ^Superscript^\n- [Links](https://svelte-streamdown.beynar.workers.dev/)\n- Headings (H1–H6)\n- Blockquotes\n- Github alert\n- Ordered \u0026 unordered lists (including roman, alpha, nested)\n- Task lists ([ ] and [x])\n- Code blocks\n- Mermaid diagrams\n- Math $expressions$\n- Escaping currency symbols ($140)\n- Complex tables\n- Footnotes [^1]\n- Inline citations [ref] [ref2]\n- MDX components (embed custom Svelte components)\n\n[^1]:\n    Reference render in a popover by default.\n    with _rich_ **content** support\n    and multiline\n\n\u003e [!NOTE]\n\u003e 🧠 **AI Prompting Tip:** For best results, use our [comprehensive prompt](/prompting) covering all supported markdown features.\n\n\n### 💻 Interactive Code Blocks\n\n- Syntax highlighting powered by Shiki\n- Copy-to-clipboard functionality\n- Support any Shiki themes\n\n### 🔢 Mathematical Expressions\n\nLaTeX math support through KaTeX:\n\n- Perfect rendering for scientific content\n- Inline math: $E = mc^2$\n- Block math:\n\n$$\n\\\\sum_{i=1}^n x_i\n$$\n\n### 🧜‍♀️ Mermaid Diagrams\n\n- Render Mermaid diagrams from code blocks\n- **Incremental rendering** during streaming content\n- Pan and Zoom\n- Full screen mode\n\n**Example:**\n\n```mermaid\ngraph TD\n    A[Start] --\u003e B{Is it working?}\n    B --\u003e|Yes| C[Great!]\n    B --\u003e|No| D[Debug]\n    D --\u003e B\n    C --\u003e E[End]\n```\n\n```mermaid\nsequenceDiagram\n    participant User\n    participant Frontend\n    participant API\n    participant Database\n\n    User-\u003e\u003eFrontend: Submit form\n    Frontend-\u003e\u003eAPI: POST /api/data\n    API-\u003e\u003eDatabase: INSERT query\n    Database--\u003e\u003eAPI: Success\n    API--\u003e\u003eFrontend: 200 OK\n    Frontend--\u003e\u003eUser: Show success message\n```\n\n```mermaid\npie title Project Time Allocation\n    \"Development\" : 45\n    \"Testing\" : 25\n    \"Documentation\" : 15\n    \"Meetings\" : 15\n```\n\n### Complex table support\n\n#### Colspan\n\n| H1                        | H2  | H3  |\n| ------------------------- | --- | --- |\n| This cell spans 3 columns |||\n\n| Header 1                  | Header 2 | Header 3 |\n| ------------------------- | -------- | -------- |\n| This cell spans 2 columns || Normal   |\n| Normal                    | Normal   | Normal   |\n\n#### Rowspan\n\n| Header 1        | Header 2 |\n| --------------- | -------- |\n| This cell spans | Cell A   |\n| two rows ^      | Cell B   |\n\n#### Footer\n\n| Header 1        | Header 2 |\n| --------------- | -------- |\n| Cell B          | Cell A   |\n| --------------- | -------- |\n| Footer          ||\n\n#### Column alignment\n\n| Left | Center | Right |\n| :--- | :----: | ----: |\n| A    |   B    |     C |\n\n#### Multiple headers and very complex layout\n\n| Product Category ||| Sales Data Q1-Q4 2024 ||||\n| Product | Region || Q1 | Q2 | Q3 | Q4 |\n| Name | Type | Area | Revenue | Revenue | Revenue | Revenue |\n|-------------|---------|------------|---------|---------|---------|---------|\n| Laptop Pro | Electronics | North America || $45,000 | $52,000 | $48,000 |\n| Laptop Pro ^ | ^ | Europe | $32,000 | $38,000 | $41,000 | $44,000 |\n| Laptop Pro ^ | ^ | Asia || $28,000 | $35,000 | $42,000 |\n| Office Chair | Furniture | North America | $15,000 | $18,000 | $16,000 | $17,000 |\n| Office Chair ^ | ^ | Europe | $12,000 | $14,000 | $15,000 | $16,000 |\n| Wireless Mouse | Electronics | Global ||| $25,000 | $28,000 |\n|-------------|---------|------------|---------|---------|---------|---------|\n| **Total Revenue** ||| **$152,000** | **$185,000** | **$187,000** | **$205,000** |\n\n### Complex list support\n\n#### decimal\n\n1. First item\n2. Second item\n3. Third item\n\n#### lower-alpha\n\na. First item  \nb. Second item  \nc. Third item\n\n#### upper-alpha\n\nA. First item  \nB. Second item  \nC. Third item\n\n#### lower-roman\n\ni. First item  \nii. Second item  \niii. Third item\n\n#### upper-roman\n\nI. First item  \nII. Second item  \nIII. Third item\n\n#### Nested Lists\n\n1. First level (numeric)\n   a. Second level (lowercase alpha)\n   i. Third level (lowercase roman) - Fourth level (bullet)\n   I. Fifth level (uppercase roman)\n   A. Sixth level (uppercase alpha)\n\n2. Back to the first level\n\n#### Task List\n\n- [ ] Uncompleted task\n- [x] Completed task\n- [ ] Another uncompleted task\n  - [ ] Nested uncompleted subtask\n  - [x] Nested completed subtask\n\n### Alert Support\n\n\u003e [!IMPORTANT]\n\u003e Native support for Github style Alert\n\n### Description List\n\n    :   Topic 1   :  Description 1\n    : **Topic 2** : *Description 2*\n    :   Topic 3   :  Description 3\n    :   Topic 3   :  Description 3\n\n### Citation Support\n\nStreamdown supports inline citations that allow you to reference external sources and display them in interactive popovers. Citations work out-of-the-box with a simple object structure and support nested references like this `[cloudflare.website, vercel]` will render into [cloudflare.website, vercel]\n\nTo enable inline citations, pass a `sources` object as a prop to the `Streamdown` component.\n\n#### Basic Usage\n\n```svelte\n\u003cscript\u003e\n  import { Streamdown } from 'svelte-streamdown';\n\n  let content = `According to [smith2023], AI is advancing rapidly. See also [nested.subsection] for related work.`;\n\n  let sources = {\n    \"smith2023\": {\n      title: \"AI Research Paper\",\n      url: \"https://example.com/paper\",\n      content: \"Detailed content of the citation...\"\n    },\n    \"nested\": {\n      \"subsection\": {\n        title: \"Nested Citation\",\n        url: \"https://example.com/nested\"\n      }\n    }\n  };\n\u003c/script\u003e\n\n\u003cStreamdown {content} {sources} /\u003e\n```\n\n#### Default Citation Structure\n\nCitations work with objects containing these properties:\n\n- `title (or name or author)`: Display title for the citation\n- `url (or href, url, link or source)`: Link to the source\n- `content (or text, summary or excerpt)`: Rich content to display in carousel mode\n\n\n#### Display Modes\n\nStreamdown offers two ways to display citations:\n\n- **List View**: Shows all citations in a compact list format\n- **Carousel View** (default): Step-through navigation for multiple citations with full content display\n\nYou can control the display mode using the `inlineCitationsMode` prop:\n\n```svelte\n\u003c!-- List view --\u003e\n\u003cStreamdown {content} {sources} inlineCitationsMode=\"list\" /\u003e\n\n\u003c!-- Carousel view (default) --\u003e\n\u003cStreamdown {content} {sources} inlineCitationsMode=\"carousel\" /\u003e\n```\n\n#### Citation Popovers\n\nCitations appear as clickable buttons that open popovers when clicked. The popover shows:\n- Source title and URL (when available)\n- Favicon from the source domain\n- Rich content (in carousel mode)\n- Navigation controls (in carousel mode for multiple citations)\n\n#### Custom Citation Rendering\n\nIf your citation data structure doesn't match the default format, you can customize how citations are rendered using `inlineCitationPreview`, `inlineCitationContent` or `inlineCitationPopover` snippets:\n\n```svelte\n\u003cStreamdown {content} {sources}\u003e\n  {#snippet inlineCitationPreview({ token })}\n    \u003c!-- Customize the clickable citation button --\u003e   \n      {token.keys[0]}\n  {/snippet}\n\n  {#snippet inlineCitationContent({ source, key, token })}\n    \u003c!-- Customize content displayed in popover --\u003e\n    \u003cdiv class=\"custom-content\"\u003e\n      \u003ch4\u003e{source.customTitle || key}\u003c/h4\u003e\n      \u003cp\u003e{source.customDescription}\u003c/p\u003e\n    \u003c/div\u003e\n  {/snippet}\n\u003c/Streamdown\u003e\n```\n\nThese snippets allow you to:\n- **`inlineCitationPreview`**: Customize the content of the clickable button that appears in the text\n- **`inlineCitationContent`**: Customize how individual citation content is displayed within popovers\n- **`inlineCitationPopover`**: Completely customize the list of citations\n\n## 🔄 Differences from Original React Version\n\nThis Svelte port maintains feature parity with the original [Streamdown](https://streamdown.ai/) while adapting to Svelte's patterns:\n\n| Aspect            | Original (React)         | Svelte Port               |\n| ----------------- | ------------------------ | ------------------------- |\n| **Framework**     | React                    | Svelte 5                  |\n| **Component API** | JSX Components           | Svelte Snippets           |\n| **Styling**       | Tailwind CSS             | Tailwind CSS (compatible) |\n| **Context**       | React Context            | Svelte Context            |\n| **Build System**  | Vite/React               | Vite/SvelteKit            |\n| **TypeScript**    | Full TS support          | Full TS support           |\n| **Engine**        | Remark / Rehype + marked | marked only               |\n\n### Tailwind CSS Setup\n\n\u003e [!NOTE]\n\u003e 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:\n\u003e 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.\n\u003e\n\u003e This ensures that all Streamdown's default styling is included in your Tailwind build process.\n\n```css\n@import 'tailwindcss';\n/* Add Streamdown styles to your Tailwind build */\n@source \"../node_modules/svelte-streamdown/**/*\";\n```\n\n## 🎭 Animation System\n\nStreamdown includes an animation system designed specifically for streaming AI content, providing smooth and engaging visual feedback as text appears on screen.\n\n### How It Works\n\nThe animation system works by:\n\n1. **Tokenization**: Text is broken down into tokens (words or characters) based on your configuration\n2. **Sequential Animation**: Each token animates as it is received\n3. **Block-level Animation**: Entire blocks (paragraphs, headings, code blocks) animate as units\n\n### Animation Types\n\nChoose from 4 distinct animation styles:\n\n#### `fade`\n\nA clean fade-in effect where text smoothly appears from transparent to opaque.\n\n#### `blur`\n\nText starts slightly blurred and comes into focus while fading in, creating a smooth reveal effect.\n\n#### `slideUp`\n\nText slides up from below while fading in, creating a dynamic upward motion.\n\n#### `slideDown`\n\nText slides down from above while fading in, creating a dynamic downward motion.\n\n\u003e [!TIP]\n\u003e For production applications where the LLM is not streaming (static content), disable animations entirely by setting `animation.enabled = false` to minimize DOM elements and improve performance.\n\u003e\n\u003e If using AI SDK mind to smooth stream the content to using word-level tokenization to avoid partial words not being animated.\n\n\u003e [!WARNING]\n\u003e Character-level tokenization (`tokenize: 'char'`) creates significantly more DOM elements than word-level tokenization. Use character tokenization sparingly and only when the typewriter effect is essential for your user experience.\n\n## 🚀 Quick Start\n\n### Basic Usage\n\n```svelte\n\u003cscript\u003e\n\timport { Streamdown } from 'svelte-streamdown';\n\n\tlet content = `# Hello World\n\nThis is a **bold** text and this is *italic*.\n\n\\`\\`\\`javascript\nconsole.log('Hello from Streamdown!');\n\\`\\`\\`\n`;\n\u003c/script\u003e\n\n\u003cStreamdown {content} /\u003e\n```\n\n### Advanced Usage with Custom Components\n\n```svelte\n\u003cscript\u003e\n\timport { Streamdown } from 'svelte-streamdown';\n\n\tlet content = `# Custom Components Example\n\nThis heading will use a custom component!`;\n\u003c/script\u003e\n\n\u003cStreamdown {content}\u003e\n\t{#snippet heading({ children })}\n\t\t\u003ch1 class=\"mb-4 text-4xl font-bold text-blue-600\"\u003e\n\t\t\t{@render children()}\n\t\t\u003c/h1\u003e\n\t{/snippet}\n\u003c/Streamdown\u003e\n```\n\n### Security Configuration\n\n```svelte\n\u003cscript\u003e\n\timport { Streamdown } from 'svelte-streamdown';\n\n\tlet markdown = `![Safe Image](https://trusted-domain.com/image.jpg)\n[Safe Link](https://trusted-domain.com/page)`;\n\u003c/script\u003e\n\n\u003cStreamdown\n\t{content}\n\tallowedImagePrefixes={['https://trusted-domain.com']}\n\tallowedLinkPrefixes={['https://trusted-domain.com']}\n/\u003e\n```\n\n## 📦 Bundle Optimization\n\nStreamdown is optimized for minimal bundle size by making heavy components **opt-in**. By default, Code blocks, Mermaid diagrams, and Math expressions render as lightweight fallbacks (plain text). To enable full functionality, import and pass the components you need:\n\n### Enabling Heavy Components\n\n```svelte\n\u003cscript\u003e\n\timport { Streamdown } from 'svelte-streamdown';\n\t// Import only the components you need\n\timport Code from 'svelte-streamdown/code';       // Shiki syntax highlighting\n\timport Mermaid from 'svelte-streamdown/mermaid'; // Mermaid diagrams\n\timport Math from 'svelte-streamdown/math';       // KaTeX math rendering\n\u003c/script\u003e\n\n\u003cStreamdown\n\t{content}\n\tcomponents={{ code: Code, mermaid: Mermaid, math: Math }}\n/\u003e\n```\n\n### Component Dependencies\n\n| Component | Import Path | Dependency | Size Impact |\n|-----------|-------------|------------|-------------|\n| `Code` | `svelte-streamdown/code` | Shiki | ~2MB (languages + themes) |\n| `Mermaid` | `svelte-streamdown/mermaid` | Mermaid.js | ~1.5MB |\n| `Math` | `svelte-streamdown/math` | KaTeX | ~300KB |\n\n\u003e [!TIP]\n\u003e Only import the components your application actually uses. If your content doesn't include code blocks, mermaid diagrams, or math expressions, you can skip those imports entirely for a much smaller bundle.\n\n### Fallback Behavior\n\nWhen a heavy component is not provided:\n- **Code blocks**: Render as plain `\u003cpre\u003e\u003ccode\u003e` without syntax highlighting\n- **Mermaid**: Renders the mermaid source as a code block\n- **Math**: Renders the raw LaTeX/math text\n\n## 📋 Props API\n\n| Prop                       | Type                                                                             | Default          | Description                                                                                                                                        |\n| -------------------------- | -------------------------------------------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `content`                  | `string`                                                                         | -                | **Required.** The markdown content to render                                                                                                       |\n| `sources`                  | `Record\u003cstring, any\u003e`                                                            | -                | Citation data object for inline citations                                                                                                          |\n| `class`                    | `string`                                                                         | -                | CSS class names for the wrapper element                                                                                                            |\n| `parseIncompleteMarkdown`  | `boolean`                                                                        | `true`           | Parse and fix incomplete markdown syntax                                                                                                           |\n| `defaultOrigin`            | `string`                                                                         | -                | Default origin for relative URLs                                                                                                                   |\n| `allowedLinkPrefixes`      | `string[]`                                                                       | `['*']`          | Allowed URL prefixes for links                                                                                                                     |\n| `allowedImagePrefixes`     | `string[]`                                                                       | `['*']`          | Allowed URL prefixes for images                                                                                                                    |\n| `skipHtml`                 | `boolean`                                                                        | -                | Skip HTML parsing entirely                                                                                                                         |\n| `unwrapDisallowed`         | `boolean`                                                                        | -                | Unwrap instead of removing disallowed elements                                                                                                     |\n| `urlTransform`             | `UrlTransform \\| null`                                                           | -                | Custom URL transformation function                                                                                                                 |\n| `theme`                    | `DeepPartial\u003cTheme\u003e`                                                             | -                | Custom theme overrides                                                                                                                             |\n| `baseTheme`                | `'tailwind' \\| 'shadcn'`                                                         | `'tailwind'`     | Base theme to use before applying overrides                                                                                                        |\n| `mergeTheme`               | `boolean`                                                                        | `true`           | Whether to merge theme with base theme                                                                                                             |\n| `shikiTheme`               | `string`                                                                         | `'github-light'` | Code highlighting theme (`github-dark` or `github-light` by default, or custom theme key)                                                          |\n| `shikiThemes`              | `Record\u003cstring, ThemeRegistration\u003e`                                              | -                | Additional themes as pre-imported objects (e.g., `{ nord: nordTheme }`)                                                                            |\n| `shikiLanguages`           | `LanguageInfo[]`                                                                 | -                | Additional syntax highlighting languages (merged with defaults)                                                                                    |\n| `mermaidConfig`            | `MermaidConfig`                                                                  | -                | Mermaid diagram configuration                                                                                                                      |\n| `katexConfig`              | `KatexOptions \\| ((inline: boolean) =\u003e KatexOptions)`                            | -                | KaTeX math rendering options                                                                                                                       |\n| `animation`                | `AnimationConfig`                                                                | -                | Animation configuration for streaming content                                                                                                      |\n| `animation.enabled`        | `boolean`                                                                        | `false`          | Enable/disable animations                                                                                                                          |\n| `animation.type`           | `'fade' \\| 'blur' \\| 'typewriter' \\| 'slideUp' \\| 'slideDown'`                   | `'blur'`         | Animation style for text appearance                                                                                                                |\n| `animation.duration`       | `number`                                                                         | `500`            | Animation duration in milliseconds                                                                                                                 |\n| `animation.timingFunction` | `'ease' \\| 'ease-in' \\| 'ease-out' \\| 'ease-in-out' \\| 'linear'`                 | `'ease-in'`      | CSS timing function for animations                                                                                                                 |\n| `animation.tokenize`       | `'word' \\| 'char'`                                                               | `'word'`         | Tokenization method for text animations                                                                                                            |\n| `animation.animateOnMount` | `boolean`                                                                        | `false`          | Run the token animation on mount or not, useful if you render the Streamdown component in the same time as the first token is receive from the LLM |\n| `extensions`               | `Array\u003cExtension\u003e`                                                               | `[]`             | Custom marked tokenizers to render special markdown blocks or inline tokens                                                                        |\n| `mdxComponents`            | `Record\u003cstring, Component\u003e`                                                      | `{}`             | Map of MDX component names to Svelte components (e.g., `{ Card, Button }`)                                                                         |\n| `components`               | `{ code?, mermaid?, math? }`                                                     | -                | Optional heavy components for syntax highlighting, diagrams, and math rendering                                                                    |\n| `children`                 | `Snippet\u003c[{token:GenericToken, streamdown: StreamdownContext, children: Snippet` | `undefined`      | Snippet used to render elements not supported by Streamdown, custom extensions, and MDX components                                                 |\n\n#### All Available Customizable Elements:\n\n**Text Elements**: `heading`, `p`, `strong`, `em`, `del`\n\n**Links \u0026 Media**: `a`, `img`\n\n**Lists**: `ul`, `ol`, `li`\n\n**Code**: `code`, `codeSpan`\n\n**Tables**: `table`, `thead`, `tbody`, `tr`, `th`, `td`, `tfoot`\n\n**Special Content**: `blockquote`, `hr`, `alert`, `mermaid`, `math`, `footnoteRef`, `inlineCitation`\n\n**MDX Components**: Handled via a single `mdx` snippet that receives `token`, `props`, and `children`. Use `token.tagName` to differentiate between components.\n\n**Note**: The above elements are **supported by Streamdown** and should be customized using individual props or the theme system. MDX components require the `mdx` snippet.\n\n## 🎨 Theming System\n\n### Built-in Themes\n\nStreamdown comes with two built-in themes:\n\n- **Default Theme**: The standard theme with gray-based colors\n- **Shadcn Theme**: A theme that uses shadcn/ui design tokens for seamless integration with shadcn-based projects\n\nBeyond 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.\n\n### Theme Structure\n\nEvery component has multiple themeable parts. For example, the `code` component has:\n\n```typescript\ncode: {\n  base: 'bg-gray-100 rounded p-2 font-mono text-sm',           // Main code block\n  container: 'my-4 w-full overflow-hidden rounded-xl border',   // Wrapper container\n  header: 'flex items-center justify-between bg-gray-100/80',  // Header with language\n  button: 'cursor-pointer p-1 text-gray-600 transition-all',   // Copy button\n  language: 'ml-1 font-mono lowercase',                        // Language label\n  pre: 'overflow-x-auto font-mono p-0 bg-gray-100/40'        // Pre element\n}\n```\n\n### Using Custom Themes\n\n```svelte\n\u003cscript\u003e\n\timport { Streamdown } from 'svelte-streamdown';\n\n\tlet content = `# Custom Theme Example\n\n\\`\\`\\`javascript\nconsole.log('Beautiful code blocks!');\n\\`\\`\\`\n\n\u003e This blockquote is also themed\n\n| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1   | Cell 2   |\n`;\n\n\t// Custom theme overrides\n\tlet customTheme = {\n\t\tcode: {\n\t\t\tcontainer: 'my-6 rounded-2xl border-2 border-purple-200 shadow-lg',\n\t\t\theader: 'bg-purple-50 text-purple-700 font-medium',\n\t\t\tbutton: 'text-purple-600 hover:text-purple-800 hover:bg-purple-100'\n\t\t},\n\t\tblockquote: {\n\t\t\tbase: 'border-l-8 border-purple-400 bg-purple-50 p-4 italic text-purple-800'\n\t\t},\n\t\ttable: {\n\t\t\tbase: 'border-purple-200 shadow-md',\n\t\t\tcontainer: 'my-6 rounded-lg overflow-hidden'\n\t\t},\n\t\tth: {\n\t\t\tbase: 'bg-purple-100 px-6 py-3 text-purple-900 font-bold'\n\t\t},\n\t\ttd: {\n\t\t\tbase: 'px-6 py-3 border-purple-100'\n\t\t}\n\t};\n\u003c/script\u003e\n\n\u003cStreamdown {content} theme={customTheme} /\u003e\n```\n\n### All Themeable Components\n\nEach component supports multiple themeable parts:\n\n**Headings (`h1`-`h6`)**: `base`\n\n**Text Elements (`p`, `strong`, `em`, `del`)**: `base`\n\n**Lists (`ul`, `ol`, `li`)**: `base`\n\n**Links (`a`)**: `base`, `blocked` (for blocked/unsafe links)\n\n**Code (`code`)**: `base`, `container`, `header`, `button`, `language`, `skeleton`, `pre`\n\n**Inline Code (`inlineCode`)**: `base`\n\n**Images (`img`)**: `container`, `base`, `downloadButton`\n\n**Tables (`table`, `thead`, `tbody`, `tr`, `th`, `td`)**: `base`, `container` (table only)\n\n**Blockquotes (`blockquote`)**: `base`\n\n**Alerts (`alert`)**: `base`, `title`, `icon`, plus type-specific styles (`note`, `tip`, `warning`, `caution`, `important`)\n\n**Mermaid (`mermaid`)**: `base`, `downloadButton`\n\n**Math (`math`, `inlineMath`)**: `base`\n\n**Other (`hr`, `sup`, `sub`)**: `base`\n\n### Theme Merging\n\nThemes 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.\n\n## 🧩 MDX Component Support\n\nStreamdown supports MDX-style JSX components, allowing you to embed custom Svelte components directly in your markdown content.\n\n### Basic Usage\n\n```svelte\n\u003cscript\u003e\n  import { Streamdown } from 'svelte-streamdown';\n  \n  let content = `\n# Using MDX Components\n\n\u003cCard title=\"Hello\" count={42}\u003e\nThis is **markdown content** inside a component!\n\u003c/Card\u003e\n\n\u003cButton label=\"Click me\" active={true} /\u003e\n`;\n\u003c/script\u003e\n\n\u003cStreamdown {content}\u003e\n  {#snippet mdx({ token, props, children })}\n    {#if token.tagName === 'Card'}\n      \u003cdiv class=\"rounded-lg border border-gray-200 p-4 shadow-sm\"\u003e\n        \u003ch3 class=\"text-xl font-bold\"\u003e{props.title}\u003c/h3\u003e\n        \u003cp class=\"text-gray-600\"\u003eCount: {props.count}\u003c/p\u003e\n        \u003cdiv class=\"mt-2\"\u003e\n          {@render children()}\n        \u003c/div\u003e\n      \u003c/div\u003e\n    {:else if token.tagName === 'Button'}\n      \u003cbutton class=\"rounded px-4 py-2 {props.active ? 'bg-blue-500 text-white' : 'bg-gray-200'}\"\u003e\n        {props.label}\n      \u003c/button\u003e\n    {:else}\n      {@render children()}\n    {/if}\n  {/snippet}\n\u003c/Streamdown\u003e\n```\n\n### Alternative: Using Svelte Components Directly\n\nInstead of using the `mdx` snippet with conditional logic, you can pass Svelte components directly using the `mdxComponents` prop:\n\n```svelte\n\u003cscript\u003e\n  import { Streamdown } from 'svelte-streamdown';\n  import Card from './Card.svelte';\n  import Button from './Button.svelte';\n  \n  let content = `\n# Using MDX Components\n\n\u003cCard title=\"Hello\" count={42}\u003e\nThis is **markdown content** inside a component!\n\u003c/Card\u003e\n\n\u003cButton label=\"Click me\" active={true} /\u003e\n`;\n\u003c/script\u003e\n\n\u003cStreamdown {content} mdxComponents={{ Card, Button }} /\u003e\n```\n\n**Your Svelte components** (`Card.svelte`, `Button.svelte`) should accept props and a `children` snippet:\n\n```svelte\n\u003c!-- Card.svelte --\u003e\n\u003cscript\u003e\n  let { title, count, children } = $props();\n\u003c/script\u003e\n\n\u003cdiv class=\"rounded-lg border border-gray-200 p-4 shadow-sm\"\u003e\n  \u003ch3 class=\"text-xl font-bold\"\u003e{title}\u003c/h3\u003e\n  \u003cp class=\"text-gray-600\"\u003eCount: {count}\u003c/p\u003e\n  \u003cdiv class=\"mt-2\"\u003e\n    {@render children()}\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n```svelte\n\u003c!-- Button.svelte --\u003e\n\u003cscript\u003e\n  let { label, active } = $props();\n\u003c/script\u003e\n\n\u003cbutton class=\"rounded px-4 py-2 {active ? 'bg-blue-500 text-white' : 'bg-gray-200'}\"\u003e\n  {label}\n\u003c/button\u003e\n```\n\nThis approach is cleaner when you have standalone component files, while the `mdx` snippet approach is better for inline component definitions or when you need shared logic across components.\n\n### Supported Syntax\n\n**Self-closing components:**\n```markdown\n\u003cComponent attr=\"value\" count={42} enabled={true} /\u003e\n```\n\n**Components with markdown children:**\n```markdown\n\u003cComponent title=\"Hello\"\u003e\n# This is a heading\nThis **markdown** content will be parsed!\n\u003c/Component\u003e\n```\n\n### Attribute Types\n\nMDX components support three attribute value types:\n\n- **Strings**: `attr=\"hello\"` → `\"hello\"`\n- **Numbers**: `count={42}` or `value={3.14}` → `42`, `3.14`\n- **Booleans**: `active={true}` or `disabled={false}` → `true`, `false`\n- **Expressions**: `value={variableName}` → `\"variableName\"` (stored as string)\n\n### Component Naming\n\n- Component names **must start with a capital letter** (PascalCase)\n- Valid: `\u003cCard /\u003e`, `\u003cMyComponent /\u003e`, `\u003cComponent123 /\u003e`\n- Invalid: `\u003ccard /\u003e`, `\u003cmyComponent /\u003e` (these are treated as HTML)\n\n### Streaming Safety\n\nMDX components are streaming-safe. Incomplete components are automatically handled during AI streaming:\n\n- Incomplete tags like `\u003cComponent attr` not rendered to prevent runtime errors\n- Unclosed components like `\u003cCard\u003econtent` are auto-closed with `\u003c/Card\u003e`\n- Malformed attributes are escaped to prevent rendering errors\n\nThis ensures your UI remains stable even when receiving partial markdown from streaming AI responses.\n\n### Component Props\n\nThe `mdx` snippet receives three parameters:\n- `token`: The full MdxToken with `tagName`, `attributes`, `selfClosing`, etc.\n- `props`: Object containing all parsed attributes (e.g., `props.title`, `props.count`)\n- `children`: Snippet containing parsed markdown content\n\nUse `token.tagName` to determine which component is being rendered:\n\n```svelte\n\u003c!-- Markdown: \u003cCard title=\"Hello\" count={5}\u003eContent\u003c/Card\u003e --\u003e\n\u003cStreamdown {content}\u003e\n  {#snippet mdx({ token, props, children })}\n    {#if token.tagName === 'Card'}\n      \u003cdiv\u003e\n        \u003ch3\u003e{props.title}\u003c/h3\u003e\n        \u003cspan\u003eCount: {props.count}\u003c/span\u003e\n        {@render children()}\n      \u003c/div\u003e\n    {:else if token.tagName === 'Alert'}\n      \u003cdiv class=\"alert alert-{props.type}\"\u003e\n        {@render children()}\n      \u003c/div\u003e\n    {:else}\n      \u003c!-- Fallback for unknown components --\u003e\n      {@render children()}\n    {/if}\n  {/snippet}\n\u003c/Streamdown\u003e\n```\n\n## 💉 Extensibility\n\nStreamdown is extensible through the use of custom extensions.\n\nAn extension is an object that has a `name`, a `level` and a `tokenizer` function.\n\n- `name`: The name of the extension\n- `level`: The level of the extension, can be `block` or `inline`\n- `tokenizer`: The tokenizer function, see [marked](https://github.com/markedjs/marked) for more information\n\nTo render the extension custom tokens, you can then simply use the `children` snippet.\n\n### Example\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Streamdown, type Extension } from 'svelte-streamdown';\n\tconst markedCollapsible: Extension = {\n\t\tname: 'collapsible',\n\t\tlevel: 'block',\n\t\ttokenizer(this, src) {\n\t\t\t// Match [detail]...[detail] blocks (case insensitive)\n\t\t\tconst detailMatch = src.match(/^\\[detail\\](.*?)\\[detail\\]/is);\n\n\t\t\tif (detailMatch) {\n\t\t\t\tconst content = detailMatch[1] || '';\n\t\t\t\tconst tokens = this.lexer.blockTokens(content);\n\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'detail',\n\t\t\t\t\traw: detailMatch[0], // The entire matched string including tags\n\t\t\t\t\ttokens\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\t};\n\u003c/script\u003e\n\n\u003cStreamdown\n\textensions={[markedCollapsible]}\n\tcontent={`\n[detail]\t\nThis is a collapsible **section**\n[detail]`}\n\u003e\n\t{#snippet children({ token, streamdown, children })}\n\t\t{#if token.type === 'detail'}\n\t\t\t\u003cdetails\u003e\n\t\t\t\t\u003csummary\u003e Detail \u003c/summary\u003e\n\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t{@render children()}\n\t\t\t\t\u003c/div\u003e\n\t\t\t\u003c/details\u003e\n\t\t{/if}\n\t{/snippet}\n\u003c/Streamdown\u003e\n```\n\n## 🛠️ Development\n\n### Setup\n\n```bash\n# Clone the repository\ngit clone \u003crepository-url\u003e\ncd svelte-streamdown\n\n# Install dependencies\npnpm install\n\n# Start development server\npnpm dev\n\n# Run tests\npnpm test\n\n# Build for production\npnpm build\n```\n\n### Building\n\n```bash\n# Build the library\npnpm build\n\n# Preview the showcase app\npnpm preview\n```\n\n## 🤝 Contributing\n\nContributions are welcome! This is a port of the original Streamdown project, so please:\n\n1. Check the [original Streamdown repository](https://github.com/vercel/streamdown) for upstream changes\n2. Ensure compatibility with the original API\n3. Maintain feature parity where possible\n4. Add tests for new features if you want\n\n## 📄 License\n\nMIT\n\n## 🙏 Acknowledgments\n\n- **Original Streamdown**: [Vercel](https://vercel.com) for creating the original React component\n- **Svelte Community**: For the amazing framework that made this port possible\n- **All Contributors**: For helping improve and maintain this project\n\n---\n\nMade with ❤️ and 🤖\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeynar%2Fsvelte-streamdown","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeynar%2Fsvelte-streamdown","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeynar%2Fsvelte-streamdown/lists"}