---
## StreamingMarkdown
Streaming-safe markdown renderer for LLM output. Parses a growing
`content` string incrementally (settled blocks are never re-rendered) and renders
to a real component tree — never through `{@html}`, so raw HTML in the source
stays inert text. Links and images pass a strict-by-default URL policy
(external images blocked unless allowlisted). Citation markers like `[1]`
resolve to CitationChip when a matching entry exists in `sources`; code fences
render through CodeBlock. Per-node-type `renderers` snippets hook in syntax
highlighting, custom links, images, or citations without adding dependencies.
**Import:** `import { StreamingMarkdown } from '@urbicon-ui/blocks';`
### Examples
```svelte
```
```svelte
```
### Variants
- size: md, sm (default: md)
### Api
| Prop | Type | Required | Default | Description |
| --- | --- | :---: | --- | --- |
| content | `string` | yes | | Markdown source. Append-only growth streams incrementally; any other change re-parses. |
| ...HTMLAttributes | `HTMLAttributes` | no | | HTML attributes (excluding: 'children') |
| ...StreamingMarkdownVariants | `VariantProps` | no | | Styling variants from StreamingMarkdownVariants |
| autolink | `boolean` | no | false | Recognize bare `https://…` autolinks (GFM-style) |
| class | `string` | no | | Additional CSS classes to apply to the StreamingMarkdown component |
| headingLevelStart | `1 | 2 | 3 | 4 | 5 | 6` | no | 1 | DOM heading level that markdown `#` maps to (deeper levels shift along, clamped at h6). Visual sizing keeps following the author's level. Set to 3 in chat surfaces so message headings stay out of the page outline. |
| linkTarget | `'_blank' | '_self'` | no | '_blank' | Target for rendered links; `rel="noopener noreferrer"` is added for `_blank` |
| preset | `string` | no | | Apply a named preset registered via ``. Prefer this over `class` overrides for reusable custom looks. |
| renderers | `MarkdownRenderers` | no | | Per-node-type snippet overrides (code highlighting, router links, lightboxes, …) |
| size | `'sm' | 'md'` | no | 'md' | Type scale: `md` inherits the surrounding font size, `sm` is compact |
| slotClasses | `Partial>` | no | | Per-slot class overrides. Slots: base | paragraph | heading1–6 | inlineCode | link | linkBlocked | image | imageBlocked | listUnordered | listOrdered | listItem | taskItem | taskCheckbox | blockquote | codeBlock | tableWrapper | table | tableRow | tableHeadCell | tableCell | hr | cursor |
| sources | `CitationSource[]` | no | | Citation sources. Ids activate `[id]` / `【id】` markers in the content, rendering them as CitationChip (1-based index follows array order). Markers without a matching id stay plain text. |
| streaming | `boolean` | no | false | Shows a pulsing cursor after the last block while the answer streams |
| tableRegionLabel | `string` | no | 'Table' | aria-label for the focusable scrollable region around tables |
| unstyled | `boolean` | no | | Remove default styles |
| urlPolicy | `MarkdownUrlPolicy` | no | | URL policy for links and images. Strict by default: links limited to http/https/mailto/tel + relative, every external image blocked. Keep the object referentially stable — a new reference re-parses the content. |
Inherited from:
- StreamingMarkdownVariants (external)
- Omit, 'children'> (omit-pattern)
### Types
```ts
interface CitationSource {
/** Stable id — matches the `[id]` marker in the streamed markdown. */
id: string;
/** Human-readable source title, always shown in the popover (and as the chip label under `citationStyle="label"`). */
title: string;
/** Source URL. Rendered as an outbound link only when it passes the `urlPolicy`; a blocked or absent URL shows title/snippet with no link. */
url?: string;
/** Short excerpt shown under the title, clamped to ~3 lines. */
snippet?: string;
}
```
```ts
interface MarkdownUrlPolicy {
/**
* Allowed link protocols (with trailing colon). Relative URLs are always
* allowed for links. @default ['http:', 'https:', 'mailto:', 'tel:']
*/
allowedLinkProtocols?: string[];
/**
* Prefix allowlist for image sources, matched against the normalized
* absolute URL. Empty (the default) blocks every external image; relative
* sources are allowed. Prefer deep, specific prefixes — broad ones are
* open-redirect bait. `data:` images are blocked unless a `data:` prefix
* is listed explicitly.
* @default []
*/
allowedImagePrefixes?: string[];
/** Observability hook — fires once per blocked URL occurrence. */
onBlocked?: (kind: 'link' | 'image', url: string) => void;
}
```
```ts
interface MarkdownRenderers {
codeBlock?: Snippet<[{ code: string; lang?: string; open?: boolean }]>;
/** `href` is already policy-checked: empty string when `blocked` is true. */
link?: Snippet<[{ href: string; title?: string; children: InlineNode[]; blocked: boolean }]>;
/** `src` is already policy-checked: empty string when `blocked` is true. */
image?: Snippet<[{ src: string; alt: string; title?: string; blocked: boolean }]>;
/** `source.url` is already policy-checked: stripped (undefined) when blocked. */
citation?: Snippet<[{ id: string; source?: CitationSource; index?: number }]>;
}
```
```ts
type InlineNode = | { kind: 'text'; text: string }
| { kind: 'strong'; children: InlineNode[] }
| { kind: 'em'; children: InlineNode[] }
| { kind: 'strike'; children: InlineNode[] }
| { kind: 'code'; text: string }
| {
kind: 'link';
/** Empty string when `blocked` is true — the original URL never reaches the DOM. */
href: string;
title?: string;
children: InlineNode[];
/** Set when the URL failed the `MarkdownUrlPolicy`; render as inert text. */
blocked?: boolean;
}
| {
kind: 'image';
/** Empty string when `blocked` is true. */
src: string;
alt: string;
title?: string;
blocked?: boolean;
}
| {
/** `[id]` / `【id】` citation marker; emitted only for ids in `MarkdownParseOptions.citationIds`. */
kind: 'citation';
id: string;
}
| { kind: 'break' }
```
```ts
type SlotNames = keyof ReturnType & string
```
```ts
type VariantProps = Omit<
Exclude[0], undefined>,
'class'
>
```
### Slots (slotClasses keys)
`base`, `paragraph`, `heading1`, `heading2`, `heading3`, `heading4`, `heading5`, `heading6`, `inlineCode`, `link`, `linkBlocked`, `image`, `imageBlocked`, `listUnordered`, `listOrdered`, `listItem`, `taskItem`, `taskCheckbox`, `blockquote`, `codeBlock`, `tableWrapper`, `table`, `tableRow`, `tableHeadCell`, `tableCell`, `hr`, `cursor`