Markdown parsing and rendering powered by the Rust comrak crate.
Elixir bindings for Comrak's parser and renderers.
Options follow Rust comrak::Options
and use keyword lists. MDExNative also accepts :sanitize,
:syntax_highlight, and :budget.
Return values
parse_document/2, markdown_to_html/2, markdown_to_xml/2, and the
document_to_* functions return {:ok, result} or {:error, exception}.
A failed or unavailable syntax highlighter is a MDExNative.SyntaxHighlightError.
Each has a bang variant, such as markdown_to_html!/2, that returns the result
or raises the exception. Invalid options raise ArgumentError from both.
The :tagfilter extension is deprecated but retains its existing behavior
for compatibility. Use :sanitize to clean rendered HTML instead.
Obsidian Flavored Markdown
These extensions are disabled by default:
extension: [ofm_comments: true]parses%%inline comments%%and multiline comments starting with%%at the beginning of a line. Comments remain in the AST, CommonMark, and XML, but are omitted from HTML.extension: [ofm_callouts: true]parses callouts such as> [!warning|metadata]- **Title**. Callouts support arbitrary types, metadata, Markdown titles, and fold markers (+expanded,-collapsed). They take precedence over:alertswhen both are enabled; syntax rejected by callouts can still be parsed as an alert. HTML exposes fold state as data attributes, not interactive folding behavior.- Either
:wikilinks_title_after_pipeor:wikilinks_title_before_pipeenables wiki links and![[url]]embeds. Embeds setWikiLink.embedtotrue, preserve!in CommonMark, and render as ordinary links in HTML. Setextension: [wikilinks_title_after_pipe: true, wikilinks_ignore_embeds: true]to leave![[url]]as literal text while still parsing[[url]].
Examples
iex> MDExNative.Comrak.markdown_to_html("# Hello")
{:ok, "<h1>Hello</h1>\n"}
iex> MDExNative.Comrak.markdown_to_html("- [x] done", extension: [tasklist: true])
{:ok, "<ul>\n<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> done</li>\n</ul>\n"}
iex> MDExNative.Comrak.anchorize("Hello World")
"hello-world"
iex> MDExNative.Comrak.markdown_to_html("Visible %%hidden%% text", extension: [ofm_comments: true])
{:ok, "<p>Visible text</p>\n"}
Summary
Types
Parsed MDExNative.Comrak AST node.
Parsed fenced code block info string.
Comrak Extension options.
Rendered HTML.
Markdown source text.
Comrak Parse options.
Comrak Render options.
Rendered CommonMark XML.
Functions
Converts text to a heading anchor.
Returns whether a URL is considered dangerous or not.
Converts a generic MDExNative.Comrak document to CommonMark.
Same as document_to_commonmark/2 but raises the error instead of returning it.
Converts a generic MDExNative.Comrak document to HTML.
Same as document_to_html/2 but raises the error instead of returning it.
Converts a generic MDExNative.Comrak document to XML.
Same as document_to_xml/2 but raises the error instead of returning it.
Converts Markdown to HTML.
Same as markdown_to_html/2 but raises the error instead of returning it.
Converts Markdown to XML.
Same as markdown_to_xml/2 but raises the error instead of returning it.
Parses a fenced code block info string into generic parts.
Parses Markdown into a generic MDExNative.Comrak AST.
Same as parse_document/2 but raises the error instead of returning it.
Types
@type ast_node() :: struct()
Parsed MDExNative.Comrak AST node.
@type code_fence_info() :: %{
language: String.t(),
metadata: String.t(),
attributes: %{required(String.t()) => String.t() | true}
}
Parsed fenced code block info string.
@type extension_options() :: keyword()
Comrak Extension options.
@type html() :: String.t()
Rendered HTML.
@type markdown() :: String.t()
Markdown source text.
@type options() :: keyword()
Comrak Options, plus :syntax_highlight, :sanitize, and :budget.
@type parse_options() :: keyword()
Comrak Parse options.
@type render_options() :: keyword()
Comrak Render options.
@type xml() :: String.t()
Rendered CommonMark XML.
Functions
@spec anchorize(String.t()) :: String.t()
Converts text to a heading anchor.
Examples
iex> MDExNative.Comrak.anchorize("Hello World")
"hello-world"
@spec dangerous_url?(String.t()) :: boolean()
Returns whether a URL is considered dangerous or not.
Calls comrak::html::dangerous_url/1.
Examples
iex> MDExNative.Comrak.dangerous_url?("javascript:alert(1)")
true
iex> MDExNative.Comrak.dangerous_url?("https://elixir-lang.org")
false
iex> MDExNative.Comrak.dangerous_url?("data:image/png;base64,AAAA")
false
@spec document_to_commonmark(MDExNative.Comrak.Document.t(), options()) :: {:ok, markdown()} | {:error, Exception.t()}
Converts a generic MDExNative.Comrak document to CommonMark.
@spec document_to_commonmark!(MDExNative.Comrak.Document.t(), options()) :: markdown()
Same as document_to_commonmark/2 but raises the error instead of returning it.
@spec document_to_html(MDExNative.Comrak.Document.t(), options()) :: {:ok, html()} | {:error, Exception.t()}
Converts a generic MDExNative.Comrak document to HTML.
@spec document_to_html!(MDExNative.Comrak.Document.t(), options()) :: html()
Same as document_to_html/2 but raises the error instead of returning it.
@spec document_to_xml(MDExNative.Comrak.Document.t(), options()) :: {:ok, xml()} | {:error, Exception.t()}
Converts a generic MDExNative.Comrak document to XML.
@spec document_to_xml!(MDExNative.Comrak.Document.t(), options()) :: xml()
Same as document_to_xml/2 but raises the error instead of returning it.
Converts Markdown to HTML.
Options
Pass Comrak options as keyword lists matching comrak::Options
or the extra :syntax_highlight and :sanitize options:
MDExNative adds two top-level options:
:extension- maps to Comrak'sExtensionoptions.:parse- maps to Comrak'sParseoptions.:render- maps to Comrak'sRenderoptions,:syntax_highlight- highlights fenced code blocks. Disabled by default.Defaults to
syntax_highlight: nil.To highlight code, compile MDExNative with a highlighter and choose the engine:
Lumis
config :mdex_native, syntax_highlighter: :lumis [engine: :lumis, opts: [formatter: {:html_inline, theme: "catppuccin_macchiato"}]]Syntect
config :mdex_native, syntax_highlighter: :syntect [engine: :syntect, opts: [theme: "Catppuccin Macchiato"]]See the Syntax highlighting guide for complete examples.
:sanitize- cleans rendered HTML. Defaults tonil.See the Sanitization guide for more info.
:budget- bounds parsing, as[parse_time: milliseconds, parse_memory: bytes].:parse_memorycaps the heap the parse holds.0ornilmeans no limit.
Examples
iex> MDExNative.Comrak.markdown_to_html("**bold**")
{:ok, "<p><strong>bold</strong></p>\n"}
iex> MDExNative.Comrak.markdown_to_html("- [x] done", extension: [tasklist: true])
{:ok, "<ul>\n<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> done</li>\n</ul>\n"}
iex> MDExNative.Comrak.markdown_to_html("<h1>Title</h1><p>Content</p>", render: [unsafe: true], sanitize: [rm_tags: ["h1"]])
{:ok, "Title<p>Content</p>\n"}
# default disabled syntax highlighter
iex> markdown = "```rust\nfn main() {}\n```"
iex> MDExNative.Comrak.markdown_to_html(markdown)
{:ok, "<pre><code class=\"language-rust\">fn main() {}\n</code></pre>\n"}
Same as markdown_to_html/2 but raises the error instead of returning it.
Converts Markdown to XML.
Examples
iex> MDExNative.Comrak.markdown_to_xml("# Hello")
{:ok, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n<document xmlns=\"http://commonmark.org/xml/1.0\">\n <heading level=\"1\">\n <text xml:space=\"preserve\">Hello</text>\n </heading>\n</document>\n"}
iex> MDExNative.Comrak.markdown_to_xml("# Hello", render: [sourcepos: true])
{:ok, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n<document sourcepos=\"1:1-1:7\" xmlns=\"http://commonmark.org/xml/1.0\">\n <heading sourcepos=\"1:1-1:7\" level=\"1\">\n <text sourcepos=\"1:3-1:7\" xml:space=\"preserve\">Hello</text>\n </heading>\n</document>\n"}
Same as markdown_to_xml/2 but raises the error instead of returning it.
@spec parse_code_fence_info(String.t() | nil) :: code_fence_info()
Parses a fenced code block info string into generic parts.
The first word is returned as the language, the remaining text is preserved as metadata, and shell-like tokens in the metadata are exposed as attributes.
Examples
iex> MDExNative.Comrak.parse_code_fence_info(~s(elixir pre_class="demo" highlight_lines=2 include_highlights))
%{
language: "elixir",
metadata: ~s(pre_class="demo" highlight_lines=2 include_highlights),
attributes: %{
"pre_class" => "demo",
"highlight_lines" => "2",
"include_highlights" => true
}
}
iex> MDExNative.Comrak.parse_code_fence_info("")
%{language: "", metadata: "", attributes: %{}}
@spec parse_document(markdown(), options()) :: {:ok, MDExNative.Comrak.Document.t()} | {:error, Exception.t()}
Parses Markdown into a generic MDExNative.Comrak AST.
Examples
iex> MDExNative.Comrak.parse_document("# Hello")
{:ok,
%MDExNative.Comrak.Document{
nodes: [
%MDExNative.Comrak.Heading{
nodes: [
%MDExNative.Comrak.Text{literal: "Hello", sourcepos: %MDExNative.Comrak.Sourcepos{start: {1, 3}, end: {1, 7}}}
],
level: 1,
setext: false,
sourcepos: %MDExNative.Comrak.Sourcepos{start: {1, 1}, end: {1, 7}}
}
],
sourcepos: %MDExNative.Comrak.Sourcepos{start: {1, 1}, end: {1, 7}}
}}
@spec parse_document!(markdown(), options()) :: MDExNative.Comrak.Document.t()
Same as parse_document/2 but raises the error instead of returning it.