Reference

Entry points

Sätteri's entry points parse a source string and return a result object. They run synchronously unless a plugin has an async visitor, in which case they return a Promise of the same result (see Async plugins). Each one accepts an options argument — see Options for the full reference.

markdownToHtml

markdownToHtml(source: string, options?: CompileOptions): MarkdownToHtmlResult;

Parse Markdown and render HTML.

import { markdownToHtml } from "satteri";

const { html, frontmatter, data } = markdownToHtml("# Hello, *world*");
// html === "<h1>Hello, <em>world</em></h1>"

mdxToJs

MDX is a programming language: mdxToJs compiles it to JavaScript and evaluate runs that JavaScript. Treat MDX like code you execute — never compile or evaluate MDX from authors you don't trust.

mdxToJs(source: string, options?: MdxCompileOptions): MdxToJsResult;

Parse MDX and compile it to JavaScript module source. The compiled code is on code (not html).

import { mdxToJs } from "satteri";

const { code } = mdxToJs("# Hello\n\n<MyComponent />");

markdownToJs

markdownToJs(source: string, options?: MarkdownToJsOptions): MarkdownToJsResult;

Like mdxToJs, but the source is plain Markdown: {...} expressions, JSX tags, and import/export lines are ordinary Markdown text instead of MDX syntax. The options and result mirror mdxToJs — the MDX-specific fields all concern the compiled JS/JSX output, so they apply here too.

import { markdownToJs } from "satteri";

const { code } = markdownToJs("# Hello\n\n{not an expression}");

HTML in the source has no JSX representation, so by default it is dropped: Press <kbd>Ctrl</kbd> compiles to Press Ctrl. Enable features: { rawHtml: true } to parse the HTML into real elements and keep it.

Dropping happens last, when the JSX is generated, so a HAST plugin still sees the raw nodes and can replace them with something that does compile. Only what a plugin leaves behind disappears.

This is the one place markdownToJs and markdownToHtml differ on the same input: HTML output can re-emit raw HTML verbatim, JSX output cannot — except under optimizeStatic, where a collapsed subtree is serialized back to an HTML string and any raw HTML in it rides along.

Result shape

These functions return an object, never a bare string:

interface MarkdownToHtmlResult {
  html: string; // rendered HTML
  frontmatter: Frontmatter | null;
  data: Data; // the document data bag
}

interface MdxToJsResult {
  code: string; // compiled JS module source
  frontmatter: Frontmatter | null;
  data: Data;
}

MarkdownToJsResult has the same shape as MdxToJsResult.

frontmatter is the parsed block at the top of the document, or null if there is none — see Frontmatter for its shape. data is the document data bag.

evaluate

Compile and run MDX in one step, returning the module's exports (including default, the component). Pass a JSX runtime:

import { evaluate } from "satteri";
import * as runtime from "react/jsx-runtime";

const { default: Content } = evaluate("# Hello\n\n<Sparkle />", { ...runtime });

Trees without compiling

To get a plain JavaScript AST without running plugins or rendering, use the tree functions. Each parses the source and returns a materialized tree directly (not a result object), and accepts a TreeOptions object with a features and a position option.

interface TreeOptions {
  features?: Features;
  position?: boolean;
}

markdownToMdast(source: string, options?: TreeOptions): MdastNode;
mdxToMdast(source: string, options?: TreeOptions): MdastNode;
markdownToHast(source: string, options?: TreeOptions): HastNode;
mdxToHast(source: string, options?: TreeOptions): HastNode;
htmlToHast(html: string): HastNode;
import { markdownToMdast } from "satteri";

const tree = markdownToMdast("# Hello");
tree.children[0].type; // "heading"
tree.children[0].depth; // 1

Pass position: false to skip recording node.position. Disabling positions can greatly increase performance and lower the memory usage, so it is worth passing whenever nothing downstream reads positions.

const tree = markdownToMdast(source, { position: false });

This is useful when you want Sätteri's fast native parsing but another pipeline (e.g. remark plugins and remark-stringify) for the rest. The returned tree is plain objects, yours to keep — see Node lifetime for why that matters.

htmlToHast is the exception: it parses an HTML string (not Markdown or MDX) into HAST, with the same spec-compliant parsing a browser does — malformed markup is recovered, implied elements are filled in. The result is a root wrapping the implied <html> subtree. Use it to bring existing HTML into a HAST plugin pipeline.

import { htmlToHast } from "satteri";

const tree = htmlToHast("<p>hi</p>");
tree.type; // "root"

Attributes are normalised into typed hast properties (classclassName: ["…"], disabledtrue, tabindex → a number, data-foo-bardataFooBar). One deliberate divergence from standard hast: <template> content is emitted as the element's children rather than a separate content root, so external serializers that only read content will not re-serialize it.

Reparsing raw HTML (rawHtml)

By default, inline and block HTML in Markdown is preserved as opaque raw nodes and re-emitted verbatim. Pass features: { rawHtml: true } to reparse it into structured HAST:

import { markdownToHast } from "satteri";

const tree = markdownToHast(`<div class="note">\n\n**hi**\n\n</div>`, {
  features: { rawHtml: true },
});
// <div> is now a real element wrapping the parsed <p><strong>hi</strong></p>

The whole tree is reparsed through the HTML parser, so a tag opened in one raw block and closed in another is resolved against the surrounding Markdown. Positions are not preserved through the reparse.