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. Other rehype/remark
plugins follow their own version ranges — install whichever your pipeline needs.
// src/index.ts
import { Mochi } from 'mochi-framework';
import { compile as mdsvexCompile } from 'mdsvex';
import rehypeSlug from 'rehype-slug';
import { routes } from './routes';
await Mochi.serve({
markdown: {
compile: mdsvexCompile,
rehypePlugins: [rehypeSlug],
},
routes,
});With markdown configured, .md and .svx files compile through the supplied
pipeline and can be used anywhere a .svelte component is accepted — including
as a Mochi.page() route target:
// src/routes.ts
import { Mochi } from 'mochi-framework';
export const routes = {
'/about': Mochi.page('./src/about.md'),
};Markdown can embed Svelte syntax — a top-level <script> block, $props, and {expression} interpolation all work the same 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 — anything compatible with
mdsvex’s rehypePlugins and remarkPlugins works:
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
markdown: {
compile: mdsvexCompile,
rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings],
remarkPlugins: [],
}Syntax highlighting
Fenced code blocks are passed through unchanged unless you supply markdown.highlight.highlighter. Install a highlighting engine (Shiki,
highlight.js, Prism, etc.) and build a highlighter with the framework’s createHighlighter factory — it adds the code-block wrapper, copy button,
and Svelte-brace escape around the engine’s output.
bun add shiki// src/lib/highlightCode.ts
import { createHighlighter as createShiki } from 'shiki';
import { createHighlighter } from 'mochi-framework/highlight';
const shiki = await createShiki({
themes: ['vitesse-dark'],
langs: ['typescript', 'bash'],
});
export const highlightCode = createHighlighter((code, lang) => shiki.codeToHtml(code, { lang, theme: 'vitesse-dark' }));// src/index.ts
import { highlightCode } from './lib/highlightCode';
markdown: {
compile: mdsvexCompile,
highlight: { highlighter: (code, lang) => highlightCode(code, lang) },
}highlightCode is also usable directly in pages and components for
snippets outside the markdown pipeline.
createHighlighter accepts any (code, lang) => string | Promise<string> function, so you can plug in highlight.js, Prism, or a custom engine the
same way.
Islands in markdown
mochi:hydrate, mochi:hydrate:visible, mochi:defer, and mochi:defer:visible all 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} />