Packages
html_to_markdown
2.13.0
3.9.0
3.8.3
3.8.2
3.8.1
3.8.0
3.8.0-rc.2
3.8.0-rc.1
3.7.2
3.7.1
3.7.0
3.6.21
3.6.20
3.6.19
3.6.18
3.6.17
3.6.16
3.6.15
3.6.14
3.6.13
3.6.11
3.6.10
3.6.9
3.6.8
3.6.3
3.6.1
3.6.0
3.6.0-rc.25
3.6.0-rc.23
3.6.0-rc.22
3.6.0-rc.21
3.6.0-rc.20
3.6.0-rc.17
3.6.0-rc.16
3.6.0-rc.14
3.6.0-rc.13
3.6.0-rc.12
3.6.0-rc.11
3.6.0-rc.10
3.6.0-rc.9
3.6.0-rc.7
3.6.0-rc.3
3.6.0-rc.2
3.6.0-rc.1
3.5.7
3.5.5
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.4.0
3.4.0-rc.45
3.4.0-rc.44
3.4.0-rc.42
3.4.0-rc.41
3.4.0-rc.40
3.4.0-rc.25
3.4.0-rc.24
3.4.0-rc.23
3.4.0-rc.21
3.4.0-rc.20
3.4.0-rc.18
3.4.0-rc.16
3.4.0-rc.15
3.4.0-rc.14
3.4.0-rc.13
3.4.0-rc.12
3.4.0-rc.11
3.4.0-rc.9
3.4.0-rc.8
3.4.0-rc.2
3.4.0-rc.1
3.3.2
3.2.4
3.2.2
3.2.1
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
2.30.0
2.29.0
2.28.6
2.28.5
2.28.4
2.28.3
2.28.2
2.28.1
2.28.0
2.27.3
2.27.2
2.27.1
2.27.0
2.26.3
2.26.2
2.26.1
2.25.1
2.25.0
2.24.6
2.24.5
2.24.4
2.24.3
2.24.1
2.23.4
2.23.3
2.23.2
2.23.1
2.23.0
2.22.5
2.22.2
2.22.1
2.22.0
2.21.1
2.20.0
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
2.19.2
2.19.1
2.19.0
2.18.0
2.16.1
2.16.0
2.15.0
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.3
2.14.2
2.14.1
2.14.0
2.13.0
2.12.1
2.12.0
2.11.4
2.11.3
2.11.1
2.10.1
2.9.2
2.9.1
2.9.0
2.8.3
High-performance HTML to Markdown converter
Current section
Files
Jump to
Current section
Files
html_to_markdown
README.md
README.md
# HtmlToMarkdown (Elixir)
Elixir bindings for the Rust [html-to-markdown](https://github.com/Goldziher/html-to-markdown) engine.
The package exposes a fast `HTML -> Markdown` converter implemented with Rustler.
[](https://crates.io/crates/html-to-markdown-rs)
[](https://www.npmjs.com/package/html-to-markdown-node)
[](https://www.npmjs.com/package/html-to-markdown-wasm)
[](https://pypi.org/project/html-to-markdown/)
[](https://packagist.org/packages/goldziher/html-to-markdown)
[](https://rubygems.org/gems/html-to-markdown)
[](https://hex.pm/packages/html_to_markdown)
[](https://www.nuget.org/packages/Goldziher.HtmlToMarkdown/)
[](https://central.sonatype.com/artifact/io.github.goldziher/html-to-markdown)
[](https://pkg.go.dev/github.com/Goldziher/html-to-markdown/packages/go/htmltomarkdown)
[](https://github.com/Goldziher/html-to-markdown/blob/main/LICENSE)
[](https://discord.gg/pXxagNK2zN)
## Installation
Add `:html_to_markdown` to your `mix.exs` dependencies:
```elixir
def deps do
[
{:html_to_markdown, "~> 2.8"}
]
end
```
Compile the NIF (Rust and cargo are required):
```
mix deps.get
mix compile
```
## Prerequisites
- Elixir **1.19+** running on **OTP 28** (matches CI + release automation targets)
- Rust toolchain (stable) with `cargo` available
## Usage
```elixir
alias HtmlToMarkdown.{InlineImageConfig, Options}
iex> {:ok, markdown} = HtmlToMarkdown.convert("<h1>Hello</h1>")
iex> markdown
"# Hello\n"
iex> HtmlToMarkdown.convert!("<p>Example</p>", wrap: true, wrap_width: 20)
"Example\n"
# Pre-build reusable options
iex> handle = HtmlToMarkdown.options(%Options{wrap: true, wrap_width: 40})
iex> HtmlToMarkdown.convert_with_options("<p>Reusable</p>", handle)
{:ok, "Reusable\n"}
```
Supported options mirror the Rust `ConversionOptions` structure and are exposed
via the `%HtmlToMarkdown.Options{}` struct (or plain maps/keyword lists). Key
fields include:
- `heading_style`, `list_indent_type`, `newline_style`, `code_block_style` – atom
values (`:atx`, `:tabs`, `:spaces`, etc.) mirroring the Rust enums.
- `wrap` / `wrap_width` – enable CommonMark soft breaks and configure the column
width.
- `keep_inline_images_in`, `strip_tags`, `preserve_tags` – map sets or lists of
tag names that control special handling for certain nodes.
- `preprocessing` – nested `%HtmlToMarkdown.PreprocessingOptions{}` (or maps)
that toggles `:preset`, `:remove_forms`, `:remove_navigation`, etc.
- `debug` – turns on verbose tracing from the Rust core.
### Inline image extraction
`convert_with_inline_images/3` returns Markdown plus decoded image blobs and
warnings emitted during extraction:
```elixir
html = ~S(<p><img src="data:image/png;base64,..." alt="Logo"></p>)
config = %InlineImageConfig{infer_dimensions: true}
{:ok, markdown, inline_images, warnings} =
HtmlToMarkdown.convert_with_inline_images(html, %{wrap: false}, config)
Enum.each(inline_images, fn image ->
File.write!("output/#{image.filename}", image.data)
end)
```
`InlineImageConfig` can be built from a struct, map, or keyword list and accepts
`max_decoded_size_bytes`, `filename_prefix`, `capture_svg`, and
`infer_dimensions`. Invalid configs return `{:error, reason}` before any native
code runs.
Inline images are returned as `%HtmlToMarkdown.InlineImage{}` structs with the
following fields:
- `data` – raw bytes decoded from the `<img>` or inline `<svg>`.
- `format` – subtype string (for example `"png"` or `"svg"`).
- `filename` / `description` – optional DOM metadata.
- `dimensions` – `{width, height}` tuple when dimension inference is enabled.
- `source` – `"img_data_uri"` or `"svg_element"` indicating where the payload
originated.
- `attributes` – remaining DOM attributes preserved as a map.
Warnings are exposed as `%HtmlToMarkdown.InlineImageWarning{index, message}`;
use `index` to correlate warnings back to the zero-based position in the inline
image list.
## Performance (Apple M4)
Benchmarks use the shared Wikipedia + hOCR fixtures from `tools/runtime-bench`
and can be re-run locally via:
```bash
cargo run --release --manifest-path tools/runtime-bench/Cargo.toml -- --language elixir --output tools/runtime-bench/results/elixir.json
# or:
task bench:bindings -- --language elixir
```
| Document | Size | Ops/sec | Throughput |
| ---------------------- | ------ | ------- | ---------- |
| Lists (Timeline) | 129 KB | 1,463 | 184.8 MB/s |
| Tables (Countries) | 360 KB | 357 | 125.5 MB/s |
| Medium (Python) | 656 KB | 171 | 109.9 MB/s |
| Large (Rust) | 567 KB | 174 | 96.4 MB/s |
| Small (Intro) | 463 KB | 247 | 111.9 MB/s |
| HOCR German PDF | 44 KB | 3,113 | 132.8 MB/s |
| HOCR Embedded Tables | 37 KB | 3,366 | 122.2 MB/s |
| HOCR Invoice | 4 KB | 20,424 | 83.6 MB/s |
The Elixir binding matches the throughput of the Rust core since conversions
are executed inside the same NIF. The numbers above help size workloads and
match the artefacts stored under `tools/runtime-bench/results/elixir.json`.
## Testing
```bash
# From the repo root
task elixir:test
task elixir:lint
```