Svelte Markdown Preprocessor.
[!NOTE]
While the API is solid and mostly complete, some changes may still occur before the first stable release.
Ideas, suggestions and code contributions are welcome.
If you find any issues or bugs, please report them so the project can be improved.
import/export of reusable components.layout mechanism to completely customize page design.entry-level configuration adapted for all markdown files.remark and rehype.metadata.svelte:head etc. in markdown files.syntax highlighting.Svelte Markdown has been completely rewritten to take full advantage of Svelte 5 and its Runes mode.
It’s a light, simple and powerful preprocessor designed specifically for managing Markdown content within Svelte projects.
Also, it comes with zero-config setup, built-in types and a dev-friendly API.
The plan is to create online docs soon, so until its published, feel free to ask questions or share feedback in the official Discussions.
# via pnpm
pnpm add -D @sveltek/markdown
# via npm
npm install -D @sveltek/markdown
// svelte.config.js
import adapter from '@sveltejs/adapter-static'
import { svelteMarkdown } from '@sveltek/markdown'
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [svelteMarkdown()],
extensions: ['.svelte', '.md'],
kit: { adapter: adapter() },
}
export default config
// markdown.config.js
import { defineConfig } from '@sveltek/markdown'
export const markdownConfig = defineConfig({
frontmatter: {
defaults: {
layout: 'default',
author: {
name: 'Sveltek',
url: 'https://github.com/sveltek',
},
// other global data...
},
},
layouts: {
default: {
path: 'lib/content/layouts/default/layout.svelte',
},
blog: {
path: 'lib/content/layouts/blog/layout.svelte',
plugins: {
remark: [],
rehype: [],
},
},
// other layouts...
},
})
Import the config to the svelte.config.js file:
// svelte.config.js
import adapter from '@sveltejs/adapter-static'
import { svelteMarkdown } from '@sveltek/markdown'
import { markdownConfig } from './markdown.config.js'
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [svelteMarkdown(markdownConfig)],
extensions: ['.svelte', '.md'],
kit: { adapter: adapter() },
}
export default config
If you work with TypeScript and Markdown components, you can define types to avoid potential issues when importing .md into .svelte files.
// src/app.d.ts
declare global {
namespace App {
declare module '*.md' {
import type { Component } from 'svelte'
declare const MarkdownComponent: Component
export default MarkdownComponent
}
}
}
export {}
Now you can import .md file into .svelte without type errors:
<!-- +page.svelte -->
<script lang="ts">
import Comp from '$lib/content/components/comp.md'
</script>
<Comp />
[!NOTE]
More examples will be added to the online docs.
Explore the playground to see more details.
---
title: Page Title
---
<script lang="ts">
import { Component } from '$lib/components'
</script>
<Component />
<Component prop="data" />
<Component>
Children content
</Component>
Content...
---
title: Page Title
---
<script lang="ts">
import { Component } from '$lib/components'
</script>
::Component
::Component prop="data"
::Component
Children content
::
Content...
---
title: Page Title
layout: default
---
Content...
---
layout: false
---
Content...
---
title: Page Title
entry: blog
---
Content...
---
entry: false
---
Content...
---
title: About page
description: Svelte Markdown Preprocessor.
layout: false
specialElements: true
---
<svelte:head>
<title>Custom Title - {title}</title>
<meta name="description" content={`Custom Description - ${description}`} />
</svelte:head>
<style>
p {
opacity: 0.6;
font-family: monospace;
font-size: 1.125rem;
}
</style>
{description}
Content...
Recommended way is to simply import the official rehypeShiki plugin from @sveltek/unplugins.
It's super flexible and easy, you can apply it to just one page, to specific layouts, or to all pages if needed.
Plugin works without additional configuration, but if you want you can configure it further via plugin options.
Install the required dependencies before use.
pnpm add -D @sveltek/unplugins shiki
import { svelteMarkdown } from '@sveltek/markdown'
import { rehypeShiki } from '@sveltek/unplugins'
svelteMarkdown({
plugins: {
rehype: [[rehypeShiki, { theme: 'github-light-default' }]],
},
})
It is also possible to use the highlight option which is a powerful way if you need more advanced configuration, but this require manual setup. Keep in mind that this applies to all pages, so it may not be desirable in every case.
Install the required dependencies before use.
pnpm add -D shiki
import { createHighlighter } from 'shiki'
const theme = 'github-dark-default'
const highlighter = await createHighlighter({
themes: [theme],
langs: ['javascript', 'typescript', 'svelte'],
})
svelteMarkdown({
highlight: {
highlighter: async ({ lang, code }) => {
return highlighter.codeToHtml(code, { lang, theme })
},
},
})
Install the required dependencies before use.
pnpm add -D @sveltek/unplugins
import { remarkToc } from '@sveltek/unplugins'
svelteMarkdown({
plugins: {
remark: [remarkToc],
},
})
Usage in markdown page:
---
title: Blog page
description: Read the latest news.
---
## What's New
## Featured Posts
<ul>
{#each frontmatter.toc as toc}
<li><a href="#{toc.id}">{toc.value}</a></li>
{/each}
</ul>
Install the required dependencies before use.
pnpm add -D @sveltek/unplugins
import { remarkReadingStats } from '@sveltek/unplugins'
svelteMarkdown({
plugins: {
remark: [remarkReadingStats],
},
})
Usage in markdown page:
---
title: Page title
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Reading Stats {JSON.stringify(readingStats)}
Reading Time: {readingStats.text}
import { svelteMarkdown, defineConfig, compile } from '@sveltek/markdown'
import { escapeSvelte } from '@sveltek/markdown/utils'
function svelteMarkdown(config?: MarkdownConfig): PreprocessorGroupimport { svelteMarkdown } from '@sveltek/markdown'
svelteMarkdown(config)
function defineConfig(config: MarkdownConfig): MarkdownConfigimport { defineConfig } from '@sveltek/markdown'
defineConfig(config)
function compile(source: string, options: CompileOptions): Promise<Processed>import { compile } from '@sveltek/markdown'
compile(source, options)
function escapeSvelte(value: string): stringimport { escapeSvelte } from '@sveltek/markdown/utils'
escapeSvelte(value)
Imports all types from the main package.
import type {
ASTScript,
CompileOptions,
Entries,
Entry,
FileData,
Frontmatter,
Highlight,
Layout,
Layouts,
MarkdownConfig,
Plugin,
PluginList,
Plugins,
} from '@sveltek/markdown'
All options are documented with descriptions and examples so autocompletion will be offered as you type. Simply hover over the property and see what it does in the quick info tooltip.
string[]['.md']Specifies custom file extensions.
svelteMarkdown({
extensions: ['.md'],
})
PreprocessorGroup[]undefinedSpecifies a custom list of preprocessors that will be applied to a Svelte file.
svelteMarkdown({
preprocessors: [vitePreprocess()],
})
{ remark: [], rehype: [] }undefinedSpecifies the top-level plugins that will be used for all markdown files.
plugins → layout.plugins → entry.pluginssvelteMarkdown({
plugins: {
remark: [], // Specifies custom `remark` plugins at the top-level (optional).
rehype: [], // Specifies custom `rehype` plugins at the top-level (optional).
},
})
Also, plugins can be disabled at the file-level:
---
title: Page title
plugins:
remark: false # Disables remark plugins for this file only
rehype: false # Disables rehype plugins for this file only
---
Content...
Record<string, Layout>undefinedSpecifies a custom layout records.
Layout component serves as a wrapper for the markdown files, which means the page content is displayed via the component's children prop.
plugins → layout.plugins → entry.pluginssvelteMarkdown({
layouts: {
default: {
path: 'lib/content/layouts/default/layout.svelte', // Specifies the path to the layout file (required).
plugins: {
remark: [], // Specifies custom `remark` plugins at the layout-level (optional).
rehype: [], // Specifies custom `rehype` plugins at the layout-level (optional).
},
},
blog: {
path: 'lib/content/layouts/blog/layout.svelte',
},
},
})
Can be enabled at the top-level (via config) or at the file-level (via frontmatter).
File-level
---
title: Page title
layout: blog
---
Content...
Also, layout plugins can be disabled at the file-level:
---
title: Page title
layout:
name: blog
plugins:
remark: false # Disables remark layout plugins for this file only
rehype: false # Disables rehype layout plugins for this file only
---
Content...
Config
svelteMarkdown({
frontmatter: {
defaults: {
layout: 'default',
},
},
})
Record<string, Entry>undefinedSpecifies a custom entry records.
Entry serves as a special configuration for markdown files, which means it is similar to layout but without the need to create a custom component file.
Allows unique and straightforward customization for an individual markdown file. An entry can be a page or a component.
plugins → layout.plugins → entry.pluginssvelteMarkdown({
entries: {
blog: {
plugins: {
remark: [], // Specifies custom `remark` plugins at the entry-level (optional).
rehype: [], // Specifies custom `rehype` plugins at the entry-level (optional).
},
},
},
})
Can be enabled at the top-level (via config) or at the file-level (via frontmatter).
File-level
---
title: Page title
entry: blog
---
Content...
Also, entry plugins can be disabled at the file-level:
---
title: Page title
entry:
name: blog
plugins:
remark: false # Disables remark entry plugins for this file only
rehype: false # Disables rehype entry plugins for this file only
---
Content...
Config
svelteMarkdown({
frontmatter: {
defaults: {
entry: 'default',
},
},
})
objectundefinedDefines frontmatter custom options.
By default, frontmatter only supports the YAML format, but allows additional customization via parser.
Record<string, unknown>undefinedSpecifies frontmatter global data to be applied to all markdown files.
svelteMarkdown({
frontmatter: {
defaults: {
author: 'Sveltek',
},
},
})
string-Specifies the start/end symbols for the frontmatter content block.
It only works in combination with the default parser.
svelteMarkdown({
frontmatter: {
marker: '+',
},
})
(value: string) => Record<string, unknown> | voidundefinedSpecifies a custom parser for frontmatter.
Allows adaptation to other formats such as TOML or JSON.
svelteMarkdown({
frontmatter: {
parser: (file) => {
// ...
},
},
})
objectundefinedDefines custom syntax highlighting options.
(data: HighlightData) => Promise<string | undefined> | string | undefinedundefinedSpecifies custom syntax highlighter.
svelteMarkdown({
highlight: {
highlighter: async ({ lang, meta, code }) => {
// ...
return code
},
},
})
booleanundefinedSpecifies support for parsing Svelte special elements such as svelte:head etc. in markdown files.
Can be enabled at the top-level (via config) or at the file-level (via frontmatter).
If you don't plan to use them in every markdown file, it is recommended to enable the option only on those pages where you really need it.
File-level
---
title: Page title
specialElements: true
---
<svelte:head>...</svelte:head>
Content...
Config
svelteMarkdown({
frontmatter: {
defaults: {
specialElements: true,
},
},
})
Check out the quick guide for more info.
Developed in 🇭🇷 Croatia, © Sveltek.
Released under the MIT license.