Markdown for Elixir. Fast, Extensible, Phoenix-native. AI-ready. Built on top of comrak, ammonia, and lumis.
447
stars
608
commits
Elixir
primary language
Sep 10, 2026
updated
Add :mdex dependency:
def deps do
[
{:mdex, "~> 0.12"}
]
end
Or use Igniter:
mix igniter.install mdex
iex> MDEx.to_html!("# Hello :smile:", extension: [shortcodes: true])
"<h1>Hello 😄</h1>"
Syntax highlight code blocks using either Lumis or Syntect, for example to use Lumis:
def deps do
[
{:mdex, "~> 0.12"},
{:lumis, "~> 0.1"}
]
end
config :mdex_native, syntax_highlighter: :lumis
Using the :html_multi_themes syntax highlighting formatter is not required but you get light/dark using it.
Mix.install([
{:mdex_gfm, "~> 0.1"}
])
markdown = """
- [x] Set up project
- [ ] Write docs
```elixir
spawn(fn -> send(current, {self(), 1 + 2}) end)
```
"""
MDEx.new(
markdown: markdown,
syntax_highlight: [
engine: :lumis,
opts: [
formatter: {:html_multi_themes,
themes: [light: "github_light", dark: "github_dark"],
default_theme: "light-dark()"}
]
]
)
|> MDExGFM.attach()
|> MDEx.to_html!()
iex> import MDEx.Sigil
iex> ~MD[# Hello :smile:]HTML
"<h1>Hello 😄</h1>"
iex> import MDEx.Sigil
iex> assigns = %{project: "MDEx"}
iex> ~MD[# {@project}]HEEX
%Phoenix.LiveView.Rendered{...}
iex> import MDEx.Sigil
iex> ~MD[# Hello :smile:]
#MDEx.Document(3 nodes)<
├── 1 [heading] level: 1, setext: false
│ ├── 2 [text] literal: "Hello "
│ └── 3 [short_code] code: "smile", emoji: "😄"
>
iex> MDEx.new(streaming: true)
...> |> MDEx.Document.put_markdown("**Install")
...> |> MDEx.to_html!()
"<p><strong>Install</strong></p>"
In docs you can find Livebook examples covering options and usage, and Guides for more info.
The library is built on top of:
Are you using MDEx and want to list your project here? Please send a PR!
💜 Support MDEx Development
If you or your company find MDEx useful, please consider sponsoring its development.
Your support helps maintain and improve MDEx for the entire Elixir community!
Current and previous sponsors
MDEx was born out of the necessity of parsing CommonMark files, to parse hundreds of files quickly, and to be easily extensible by consumers of the library.
| Feature | MDEx | mdex_native | Earmark | md | cmark | erlang-markdown |
|---|---|---|---|---|---|---|
| Active | ✅ | ✅ | ⚠️⁵ | ✅ | ❌ | ✅ |
| Pure Elixir | ⚠️⁶ | ❌ | ✅ | ✅ | ❌ | ✅ |
| Extensible | ✅ | ❌ | ✅ | ✅ | ❌ | ⚠️⁹ |
| Syntax Highlighting | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Code Block Decorators | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Streaming (fragments) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Phoenix HEEx components | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| MDX (JS) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Kramdown attributes | ❌ | ❌ | ✅⁸ | ❌ | ❌ | ❌ |
| EEx support | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Custom syntax DSL | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| AST | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| AST to Markdown | ✅ | ✅ | ⚠️⁷ | ❌ | ❌ | ❌ |
| To HTML | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| To JSON | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| To XML | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| To Manpage | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| To LaTeX | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| To Quill Delta | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| To Slack | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Emoji | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| GFM² | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| GLFM³ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Discord⁴ | ⚠️¹ | ⚠️¹ | ❌ | ❌ | ❌ | ❌ |
A benchmark is available to compare existing libs:
Name ips average deviation median 99th %
cmark 8681.81 0.115 ms ±50.21% 0.111 ms 0.161 ms
mdex_native 6560.90 0.152 ms ±7.26% 0.149 ms 0.199 ms
mdex 395.26 2.53 ms ±10.58% 2.49 ms 2.98 ms
md 278.94 3.59 ms ±8.62% 3.55 ms 3.91 ms
earmark 69.86 14.32 ms ±6.02% 14.24 ms 15.41 ms
erlang-markdown 4.80 208.43 ms ±0.75% 208.19 ms 213.13 ms
Comparison:
cmark 8681.81
mdex_native 6560.90 - 1.32x slower +0.0372 ms
mdex 395.26 - 21.96x slower +2.41 ms
md 278.94 - 31.12x slower +3.47 ms
earmark 69.86 - 124.28x slower +14.20 ms
erlang-markdown 4.80 - 1809.56x slower +208.32 ms
Memory usage statistics:
Name average deviation median 99th %
cmark 0.00006 MB ±0.00% 0.00006 MB 0.00006 MB
mdex_native 0.00008 MB ±0.00% 0.00008 MB 0.00008 MB
mdex 2.36 MB ±0.00% 2.36 MB 2.36 MB
md 11.77 MB ±0.00% 11.77 MB 11.77 MB
earmark 7.91 MB ±0.00% 7.91 MB 7.91 MB
erlang-markdown 956.16 MB ±0.00% 956.16 MB 956.16 MB
Comparison:
cmark 0.00006 MB
mdex_native 0.00008 MB - 1.38x memory usage +0.00002 MB
mdex 2.36 MB - 38615.38x memory usage +2.36 MB
md 11.77 MB - 192838.89x memory usage +11.77 MB
earmark 7.91 MB - 129581.63x memory usage +7.91 MB
erlang-markdown 956.16 MB - 15665687.31x memory usage +956.16 MB
The most performance gain is using the ~MD sigil to compile the Markdown instead of parsing it at runtime,
prefer using it when possible.
To finish, a friendly reminder that all libs have their own strengths and trade-offs so use the one that better suits your needs.
Elixir
99.9%
Markdown for Elixir. Fast, Extensible, Phoenix-native. AI-ready. Built on top of comrak, ammonia, and lumis.
447
stars
608
commits
Elixir
primary language
Sep 10, 2026
updated
Add :mdex dependency:
def deps do
[
{:mdex, "~> 0.12"}
]
end
Or use Igniter:
mix igniter.install mdex
iex> MDEx.to_html!("# Hello :smile:", extension: [shortcodes: true])
"<h1>Hello 😄</h1>"
Syntax highlight code blocks using either Lumis or Syntect, for example to use Lumis:
def deps do
[
{:mdex, "~> 0.12"},
{:lumis, "~> 0.1"}
]
end
config :mdex_native, syntax_highlighter: :lumis
Using the :html_multi_themes syntax highlighting formatter is not required but you get light/dark using it.
Mix.install([
{:mdex_gfm, "~> 0.1"}
])
markdown = """
- [x] Set up project
- [ ] Write docs
```elixir
spawn(fn -> send(current, {self(), 1 + 2}) end)
```
"""
MDEx.new(
markdown: markdown,
syntax_highlight: [
engine: :lumis,
opts: [
formatter: {:html_multi_themes,
themes: [light: "github_light", dark: "github_dark"],
default_theme: "light-dark()"}
]
]
)
|> MDExGFM.attach()
|> MDEx.to_html!()
iex> import MDEx.Sigil
iex> ~MD[# Hello :smile:]HTML
"<h1>Hello 😄</h1>"
iex> import MDEx.Sigil
iex> assigns = %{project: "MDEx"}
iex> ~MD[# {@project}]HEEX
%Phoenix.LiveView.Rendered{...}
iex> import MDEx.Sigil
iex> ~MD[# Hello :smile:]
#MDEx.Document(3 nodes)<
├── 1 [heading] level: 1, setext: false
│ ├── 2 [text] literal: "Hello "
│ └── 3 [short_code] code: "smile", emoji: "😄"
>
iex> MDEx.new(streaming: true)
...> |> MDEx.Document.put_markdown("**Install")
...> |> MDEx.to_html!()
"<p><strong>Install</strong></p>"
In docs you can find Livebook examples covering options and usage, and Guides for more info.
The library is built on top of:
Are you using MDEx and want to list your project here? Please send a PR!
💜 Support MDEx Development
If you or your company find MDEx useful, please consider sponsoring its development.
Your support helps maintain and improve MDEx for the entire Elixir community!
Current and previous sponsors
MDEx was born out of the necessity of parsing CommonMark files, to parse hundreds of files quickly, and to be easily extensible by consumers of the library.
| Feature | MDEx | mdex_native | Earmark | md | cmark | erlang-markdown |
|---|---|---|---|---|---|---|
| Active | ✅ | ✅ | ⚠️⁵ | ✅ | ❌ | ✅ |
| Pure Elixir | ⚠️⁶ | ❌ | ✅ | ✅ | ❌ | ✅ |
| Extensible | ✅ | ❌ | ✅ | ✅ | ❌ | ⚠️⁹ |
| Syntax Highlighting | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Code Block Decorators | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Streaming (fragments) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Phoenix HEEx components | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| MDX (JS) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Kramdown attributes | ❌ | ❌ | ✅⁸ | ❌ | ❌ | ❌ |
| EEx support | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Custom syntax DSL | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| AST | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| AST to Markdown | ✅ | ✅ | ⚠️⁷ | ❌ | ❌ | ❌ |
| To HTML | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| To JSON | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| To XML | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| To Manpage | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| To LaTeX | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| To Quill Delta | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| To Slack | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Emoji | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| GFM² | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| GLFM³ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Discord⁴ | ⚠️¹ | ⚠️¹ | ❌ | ❌ | ❌ | ❌ |
A benchmark is available to compare existing libs:
Name ips average deviation median 99th %
cmark 8681.81 0.115 ms ±50.21% 0.111 ms 0.161 ms
mdex_native 6560.90 0.152 ms ±7.26% 0.149 ms 0.199 ms
mdex 395.26 2.53 ms ±10.58% 2.49 ms 2.98 ms
md 278.94 3.59 ms ±8.62% 3.55 ms 3.91 ms
earmark 69.86 14.32 ms ±6.02% 14.24 ms 15.41 ms
erlang-markdown 4.80 208.43 ms ±0.75% 208.19 ms 213.13 ms
Comparison:
cmark 8681.81
mdex_native 6560.90 - 1.32x slower +0.0372 ms
mdex 395.26 - 21.96x slower +2.41 ms
md 278.94 - 31.12x slower +3.47 ms
earmark 69.86 - 124.28x slower +14.20 ms
erlang-markdown 4.80 - 1809.56x slower +208.32 ms
Memory usage statistics:
Name average deviation median 99th %
cmark 0.00006 MB ±0.00% 0.00006 MB 0.00006 MB
mdex_native 0.00008 MB ±0.00% 0.00008 MB 0.00008 MB
mdex 2.36 MB ±0.00% 2.36 MB 2.36 MB
md 11.77 MB ±0.00% 11.77 MB 11.77 MB
earmark 7.91 MB ±0.00% 7.91 MB 7.91 MB
erlang-markdown 956.16 MB ±0.00% 956.16 MB 956.16 MB
Comparison:
cmark 0.00006 MB
mdex_native 0.00008 MB - 1.38x memory usage +0.00002 MB
mdex 2.36 MB - 38615.38x memory usage +2.36 MB
md 11.77 MB - 192838.89x memory usage +11.77 MB
earmark 7.91 MB - 129581.63x memory usage +7.91 MB
erlang-markdown 956.16 MB - 15665687.31x memory usage +956.16 MB
The most performance gain is using the ~MD sigil to compile the Markdown instead of parsing it at runtime,
prefer using it when possible.
To finish, a friendly reminder that all libs have their own strengths and trade-offs so use the one that better suits your needs.
Elixir
99.9%