Runtime-first Markdown rendering for Svelte.
- Parser:
markdown-it - Core output: custom AST/Blocks (not HTML strings)
- Renderer: declarative Svelte component tree (no DOM scan/mount pipeline)
pnpm add svmarkdown<script lang="ts">
import { Markdown } from 'svmarkdown'
import Link from './Link.svelte'
import Image from './Image.svelte'
import Code from './Code.svelte'
let content = $state('Visit [Svelte](https://svelte.dev) and `inline code`')
const components = {
a: Link,
img: Image,
code: Code,
}
</script>
<Markdown {content} {components} />just use ::: [component] key="value" to create a component block, and the content inside will be passed as children prop.
::: Alert type=warning title="Heads up"
This is **Markdown children**.
:::just use ```component:[component] {"key":"value"} to create a component block, and the content inside will be passed as children prop.
```component:Chart {"title":"Traffic"}
month,visits
Jan,421
Feb,530
```import { Markdown, createParser, parseMarkdown } from 'svmarkdown'parseMarkdown(markdown, options): parse once, returnsSvmdRootcreateParser(options): create a reusable parser for frequent updates<Markdown />: runtime rendering with anAST -> Svelte treeupdate path
import type { SvmdParseOptions } from 'svmarkdown'
const options: SvmdParseOptions = {
componentBlocks: {
Alert: true,
Chart: {
container: false,
fence: true,
parseFenceBodyAsMarkdown: false,
},
},
fenceComponentPrefix: 'component:',
}pnpm install
pnpm run typecheck
pnpm run test
pnpm run build
pnpm run playconst parseOptions: SvmdParseOptions = {
componentBlocks: {
Alert: true,
Note: { container: true, fence: false },
Chart: { container: false, fence: true },
Card: { fence: true, parseFenceBodyAsMarkdown: true },
},
}const parseOptions: SvmdParseOptions = {
componentBlocks: {
Alert: {
parseProps(raw) {
if (!raw) return {}
if (raw.startsWith('yaml:')) {
return { source: raw.slice(5).trim() }
}
return { text: raw }
},
},
},
}const parseOptions: SvmdParseOptions = {
fenceComponentPrefix: '@component:',
}import footnote from 'markdown-it-footnote'
import container from 'markdown-it-container'
const parseOptions: SvmdParseOptions = {
markdownItPlugins: [
footnote,
[container, 'spoiler', { marker: ':' }],
],
}import MarkdownIt from 'markdown-it'
const md = new MarkdownIt({ html: true, linkify: true })
const parseOptions: SvmdParseOptions = {
markdownIt: md,
}<Markdown
content={md}
parseOptions={{ markdownItOptions: { html: true } }}
renderOptions={{ allowDangerousHtml: true }}
/><Markdown
content={md}
renderOptions={{ softBreak: 'space' }} // 'space' | 'newline' | 'br'
/><!-- when overriding `a` -->
<script lang="ts">
export let linkLayout // 'inline' | 'standalone'
export let linkStandalone // boolean
</script><Markdown
content={md}
components={{ Alert, Chart }}
inferComponentBlocks={false}
parseOptions={{ componentBlocks: { Alert: true } }}
/><script lang="ts">
export let node
export let syntax
export let source
</script><script lang="ts">
import { SvmdChildren, parseMarkdown } from 'svmarkdown'
const ast = parseMarkdown(md)
</script>
<SvmdChildren nodes={ast.children} components={components} />MIT License