@mastrojs/markdown
July 6, 2026 ยท View on GitHub
A few simple helper functions to generate HTML from markdown. Just enough to get you started with markdown with Mastro.
By default, it uses micromark with micromark-extension-gfm under the hood.
Validate YAML frontmatter by bringing your own Standard Schema-compliant validation library (e.g. Zod, Valibot or validate.js).
Install
The easiest way to get started is to install Mastro and select the "blog" template. Alternatively:
Deno
deno add jsr:@mastrojs/markdown
Node.js
pnpm add jsr:@mastrojs/markdown
Bun
bunx jsr add @mastrojs/markdown
Usage
import { renderToString } from "@mastrojs";
import { markdownToHtml } from "@mastrojs/markdown";
const { content, meta } = await markdownToHtml(`
---
title: my title
---
hi *there*
`);
const htmlStr = await renderToString(content);
In addition to markdownToHtml(inputStr, opts), there is:
readMarkdownFile(filePath, opts)readMarkdownFiles(globPattern, opts)serveMarkdownFolder(opts, renderFn)
For a tutorial, read the chapter A static blog from markdown files in the Mastro Guide.
Options
Parse
Use the parse option to supply either a Micromark options object:
const { content, meta } = markdownToHtml(input, { parse: { allowDangerousHtml: true } });
Micromark is fairly basic (e.g. no syntax highlighting of code blocks). If you want a more feature-rich markdown engine, supply a custom markdown-to-HTML function to parse.
For example using markdown-it:
import { markdownToHtml } from "@mastrojs/markdown";
import markdownIt from "markdown-it";
const { content, meta } = markdownToHtml(input, { parse: markdownIt.render });
Or using remark-rehype:
import { markdownToHtml } from "@mastrojs/markdown";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeHighlight from "rehype-highlight";
import rehypeStringify from "rehype-stringify";
const parse = async (markdownText: string) =>
String(
await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeHighlight)
.use(rehypeStringify)
.process(markdownText)
);
const { content, meta } = markdownToHtml(input, { parse });
Schema
The default TypeScript type for the YAML metadata is Record<string, unknown>, but you can override that with e.g. readMarkdownFile<{title: string}>("post.md"). But to actually verify the metadata is correct, you should use a schema. For example using validate.js:
import { object, string } from "./validate.js";
const schema = object({
title: string,
});
const { content, meta } = await markdownToHtml(input, { schema }),