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.
Link to this headingmarkdownToHtml
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>"
Link to this headingmdxToJs
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 />");
Link to this headingmarkdownToJs
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.
Link to this headingResult 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.
Link to this headingevaluate
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 });
Link to this headingTrees 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;
hastToHtml(tree: HastNode | HastNode[]): string;
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 (class → className: ["…"], disabled → true, tabindex → a number, data-foo-bar → dataFooBar). 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.
hastToHtml goes the other way, serializing a HAST tree back to an HTML string. It takes a root (whose children are rendered), any other single node, or a list of nodes.
import { hastToHtml, htmlToHast } from "satteri";
hastToHtml({ type: "element", tagName: "p", properties: {}, children: [{ type: "text", value: "hi" }] });
// '<p>hi</p>'
hastToHtml(htmlToHast("<!doctype html><p>hi</p>"));
// '<!doctype html><html><head></head><body><p>hi</p></body></html>'
It is the renderer markdownToHtml uses, so raw nodes are emitted verbatim and MDX nodes, which have no HTML representation, are skipped. The result is the tree's exact serialization, with no trailing newline (the one markdownToHtml ends a document with).
Link to this headingReparsing 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.