From minify-html¶
minify-html is a Rust HTML minifier with bindings for Python, Node, Deno,
Ruby, Java, and a standalone CLI. minify_html.minify(html, **flags) collapses whitespace with a context-aware
sensitivity model, drops the tags and attribute quotes the WHATWG rules make optional, strips comments, and can minify
embedded CSS (via lightningcss) and JavaScript in the same pass. It targets maximum size reduction and is used to shrink
HTML at build time and in response pipelines across those language ecosystems.
turbohtml covers the same HTML folds with turbohtml.clean.minify(), one call that minifies a document over the
WHATWG tree turbohtml already builds, configured by the frozen Minify options object. It collapses
insignificant whitespace, omits the optional tags, unquotes attributes, strips comments, and optionally rewrites inline
JavaScript and CSS, running the work in a compiled C serializer over the parsed tree rather than as a separate Rust
pass.
turbohtml vs minify-html¶
Dimension |
turbohtml |
minify-html |
|---|---|---|
Scope |
Full WHATWG parser plus serializer; minify is one output layout of a queryable tree |
Standalone string-in/string-out HTML/CSS/JS minifier |
Feature breadth |
Whitespace, comment, optional-tag, and attribute-quote folds plus opt-in inline JS and CSS minification |
The same HTML folds plus in-pass CSS and JS minification, template-syntax and SSI preservation flags, and more aggressive whitespace and attribute rewrites |
Performance |
C serializer over an already-built tree; about 2x faster, parse included (see below) |
Optimized Rust minifier, no tree retained |
Typing |
Fully typed, frozen |
Untyped |
Dependencies |
Self-contained C extension, no runtime dependencies |
Prebuilt Rust wheel, no runtime Python dependencies |
Maintenance |
Actively maintained |
Actively maintained across multiple language bindings |
Feature overlap¶
These HTML folds port one-to-one; each minify-html flag becomes a field on Minify:
Whitespace collapsing — on by default in both; maps to
Minify(collapse_whitespace=True)(also the default).Comment removal —
keep_comments=False(the minify-html default) maps toMinify(strip_comments=True)(the turbohtml default; comments go unless you opt out).Optional-tag omission —
keep_closing_tagsandkeep_html_and_head_opening_tagsmap to the singleMinify(omit_optional_tags=True), which drops the start and end tags the WHATWG rules make optional.Attribute unquoting — on by default in both; maps to
Minify(unquote_attributes=True).Inline JavaScript minification —
minify_js=Truemaps toMinify(minify_js=JSMinify()), which rewrites<script>bodies with turbohtml’s shipped JS minifier.Inline CSS minification —
minify_css=Truemaps toMinify(minify_css=True), which shrinks<style>bodies andstyleattribute values with turbohtml’s shipped, value-safe CSS minifier.
What turbohtml adds¶
A real parse and a tree — the input runs through the full WHATWG algorithm, so the same call site can select, mutate, and re-serialize the document. minify-html is a one-shot string transform with no tree to query.
A typed, frozen options object —
Minifyvalidates configuration at construction and reuses one instance across calls, versus untyped keyword flags.Conservative, render-safe output — every fold is round-trip safe, so the output reparses to the input tree and minifying twice changes nothing.
Self-contained C extension — no separate Rust minifier in the toolchain; minify shares the parser turbohtml already ships.
A shell entry point —
python -m turbohtml minify(installed as theturbohtmlconsole script, with a--minify-cssflag) reads a file or stdin, covering minify-html’s standalone CLI on Python.
What minify-html has that turbohtml does not¶
Aggressive whitespace removal — minify-html’s context-aware model can delete inter-element whitespace outright where its sensitivity rules judge it insignificant. turbohtml collapses each run to a single space and never deletes it, so the output stays render-safe. No equivalent by design.
Boolean-attribute and entity shortening — minify-html rewrites
checked="checked"to a barecheckedand shortens&to&where safe. turbohtml writes boolean attributes in full and keeps&. No equivalent.Template and server-side markers —
preserve_brace_template_syntax,preserve_chevron_percent_template_syntax,keep_ssi_comments,keep_input_type_text_attr,ensure_spec_compliant_unquoted_attribute_values,remove_bangs, andremove_processing_instructionstune edge cases turbohtml does not expose knobs for. No equivalent; turbohtml applies the WHATWG-safe default in each case.Non-Python bindings — minify-html ships for Node, Deno, Ruby, and Java; turbohtml is Python-only (the standalone binary’s job is covered by
python -m turbohtml minify).
Performance¶
minify a document |
turbohtml |
|
|---|---|---|
daring fireball (10 kB) |
28.2 µs |
67.2 µs (2.4x) |
ars technica (56 kB) |
127 µs |
321 µs (2.6x) |
mozilla blog (95 kB) |
267 µs |
751 µs (2.9x) |
whatwg spec (235 kB) |
935 µs |
1.7 ms (1.9x) |
On the folds the two share (collapsing insignificant whitespace, omitting the tags the WHATWG rules make optional, dropping redundant attribute quotes, and stripping comments) turbohtml runs about twice as fast, parse included. minify-html produces smaller output because it removes whitespace and shortens attributes more aggressively; turbohtml trades those bytes for output that stays valid and reparses to the same tree.
How to migrate¶
Swap the import and drop the per-flag keywords for the options object:
# minify-html
import minify_html
minify_html.minify(html, keep_comments=False)
# turbohtml
from turbohtml.clean import minify
minify(html)
Each fold is a field on Minify, so a flag becomes one keyword on the options object:
turbohtml |
|
|---|---|
whitespace collapsing (on by default) |
|
|
|
|
|
attribute unquoting (on by default) |
|
|
|
|
|
|
the doctype is always normalized to |
|
the WHATWG algorithm has no processing instructions in the HTML namespace |
The default call minifies with every HTML fold engaged:
from turbohtml.clean import minify
print(minify("<ul> <li> one </li> <li> two </li> </ul>"))
<ul> <li> one </li> <li> two </li> </ul>
Gotchas and pitfalls¶
The two minifiers do not produce byte-for-byte identical output. Every turbohtml fold is round-trip safe, so minifying is idempotent and the output reparses to the input tree; turbohtml is the more conservative of the two, so port against behavior rather than string equality.
turbohtml keeps attributes in source order; minify-html sorts them.
turbohtml collapses each whitespace run to one space and keeps a few optional end tags (
</body>,</html>,</li>) that minify-html drops, so its output stays valid and render-safe while running a few bytes larger.turbohtml writes boolean attributes in full (
checked="checked") and keeps&where minify-html shortens to a barecheckedand&.Embedded CSS and JavaScript minification is opt-in:
Minify(minify_css=True)shrinks<style>bodies andstyleattribute values, andMinify(minify_js=...)rewrites inline<script>content. minify-html folds both in the same call and turbohtml leaves them verbatim unless asked.minifyparses the input as a full document, so a bare fragment gains the<html>/<body>structure the WHATWG algorithm infers; call it on whole pages, not detached fragments.