@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:

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 }),