SSR framework for Svelte 5 + Bun with islands-based selective hydration
On this page
MdSvex
Markdown support is opt-in. Install mdsvex and any rehype/remark plugins you want, then inject them through Mochi.serve({ markdown: ... }).
bun add mdsvex@^0.12 rehype-slug@^6Mochi is tested against mdsvex ^0.12 and rehype-slug ^6.
With markdown configured, .md and .svx files compile through the pipeline and can be used anywhere a .svelte component is accepted, including as a Mochi.page() route target:
// src/index.ts
import { Mochi } from 'mochi-framework';
import { compile as mdsvexCompile } from 'mdsvex';
import rehypeSlug from 'rehype-slug';
await Mochi.serve({
markdown: {
compile: mdsvexCompile,
rehypePlugins: [rehypeSlug],
},
routes: {
'/about': Mochi.page('./src/about.md'),
},
});Markdown can embed Svelte syntax — a top-level <script> block, $props, and {expression} interpolation work as in a .svelte file:
<script>
let { name = 'world' } = $props();
</script>
# Hello, {name}
This page was rendered at {new Date().toISOString()}.The markdown config accepts a full plugin chain compatible with mdsvex’s rehypePlugins and remarkPlugins.
Syntax highlighting
Fenced code blocks pass through unchanged unless you supply markdown.highlight.highlighter. Install a highlighting engine (Shiki, highlight.js, Prism) and build a highlighter with the framework’s createHighlighter factory. It adds the code-block wrapper, copy button, and Svelte-brace escape.
bun add shiki// src/lib/highlightCode.ts
import { createHighlighter as createShiki, createJavaScriptRegexEngine } from 'shiki';
import { createHighlighter } from 'mochi-framework/highlight';
const shiki = await createShiki({
engine: createJavaScriptRegexEngine({ forgiving: true }),
themes: ['vitesse-dark'],
langs: ['typescript', 'bash'],
});
export const highlightCode = createHighlighter((code, lang) => shiki.codeToHtml(code, { lang, theme: 'vitesse-dark' }));Mochi memoizes results per (code, lang). The cache holds 1000 snippets and evicts in insertion order. Tune it with cacheSize (0 disables memoization).
createJavaScriptRegexEngine uses the JS RegExp engine, so no WASM is loaded — Shiki’s default oniguruma WASM engine grows WebAssembly.Memory that is never reclaimed.
// src/index.ts
import { highlightCode } from './lib/highlightCode';
markdown: {
compile: mdsvexCompile,
highlight: { highlighter: (code, lang) => highlightCode(code, lang) },
}createHighlighter accepts any (code, lang) => string | Promise<string> function.
Islands in markdown
mochi:hydrate, mochi:hydrate:visible, mochi:defer, and mochi:defer:visible work on components instantiated inside a .md / .svx file. Import the component as a default import from the markdown’s top-level <script> block, then apply the directive on the tag:
<script>
import Counter from './Counter.svelte';
</script>
<Counter mochi:hydrate count={3} />See it in action
Live demos showing key concepts from this page