html-minifier-terser vs htmlnano vs @aspect-build/minify-html: HTML Minification in Node.js (2026)
TL;DR
html-minifier-terser is the most popular HTML minifier — feature-rich, handles inline CSS/JS, highly configurable, maintained fork of html-minifier. htmlnano is the modular HTML minifier — PostCSS-style plugin architecture, integrates with PostHTML, safe defaults, used by Parcel. minify-html is the Rust-based HTML minifier — blazing fast native performance, WASM and Node.js bindings, aggressive minification. In 2026: html-minifier-terser for maximum configurability, htmlnano for PostHTML/Parcel integration, minify-html for maximum speed.
Key Takeaways
- html-minifier-terser: ~10M weekly downloads — feature-rich, configurable, handles inline CSS/JS
- htmlnano: ~2M weekly downloads — modular plugins, PostHTML ecosystem, used by Parcel
- minify-html: ~500K weekly downloads — Rust/WASM, fastest performance, aggressive defaults
- All three reduce HTML file sizes by removing whitespace, comments, and redundant attributes
- html-minifier-terser is the most battle-tested and widely used
- minify-html is 5-10x faster than JS-based minifiers
html-minifier-terser
html-minifier-terser — feature-rich HTML minifier:
Basic usage
import { minify } from "html-minifier-terser"
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PkgPulse</title>
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<!-- Main content -->
<div class="container">
<h1>Compare npm packages</h1>
</div>
<script>
console.log("Hello");
</script>
</body>
</html>
`
const minified = await minify(html, {
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true,
removeRedundantAttributes: true,
removeEmptyAttributes: true,
removeOptionalTags: true,
})
// → <!DOCTYPE html><html lang=en><meta charset=UTF-8><title>PkgPulse</title><style>body{margin:0;padding:0}</style><div class=container><h1>Compare npm packages</h1></div><script>console.log("Hello")</script>
Full configuration
import { minify } from "html-minifier-terser"
const result = await minify(html, {
// Whitespace:
collapseWhitespace: true,
conservativeCollapse: false, // Don't preserve single space
collapseInlineTagWhitespace: false,
// Comments:
removeComments: true,
removeCommentsFromCDATA: true,
// Attributes:
removeRedundantAttributes: true, // Remove type="text" from input
removeEmptyAttributes: true, // Remove class=""
removeOptionalTags: true, // Remove optional closing tags
removeAttributeQuotes: true, // Remove quotes when safe
collapseBooleanAttributes: true, // disabled="disabled" → disabled
sortAttributes: true,
sortClassName: true,
// Inline CSS/JS:
minifyCSS: true, // Uses clean-css
minifyJS: true, // Uses terser
minifyURLs: true,
// HTML5:
html5: true,
useShortDoctype: true,
// Custom processing:
processScripts: ["application/ld+json"], // Minify JSON-LD
})
With build tools
// Webpack — html-webpack-plugin uses html-minifier-terser:
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
minify: {
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true,
},
}),
],
}
// Vite — vite-plugin-html:
import { createHtmlPlugin } from "vite-plugin-html"
export default {
plugins: [
createHtmlPlugin({
minify: true, // Uses html-minifier-terser internally
}),
],
}
htmlnano
htmlnano — modular HTML minifier:
Basic usage
import htmlnano from "htmlnano"
const { html } = await htmlnano.process(`
<div class="container">
<!-- Comment -->
<h1>Hello World</h1>
<img src="photo.jpg" alt="" />
</div>
`)
// → <div class="container"><h1>Hello World</h1><img src="photo.jpg" alt></div>
Preset configurations
import htmlnano from "htmlnano"
// Safe preset (default) — only safe transformations:
const safe = await htmlnano.process(html, {}, htmlnano.presets.safe)
// Aggressive preset — maximum minification:
const max = await htmlnano.process(html, {}, htmlnano.presets.max)
// Custom — cherry-pick modules:
const custom = await htmlnano.process(html, {
removeComments: "safe", // Remove non-conditional comments
collapseWhitespace: "conservative", // Safe whitespace collapse
minifyCss: true, // Minify inline CSS (uses cssnano)
minifyJs: true, // Minify inline JS (uses terser)
minifySvg: true, // Minify inline SVGs (uses svgo)
removeRedundantAttributes: true,
collapseBooleanAttributes: true,
removeEmptyAttributes: true,
deduplicateAttributeValues: true,
removeOptionalTags: false, // Keep optional tags (safer)
})
With PostHTML
import posthtml from "posthtml"
import htmlnano from "htmlnano"
// Use as PostHTML plugin:
const result = await posthtml([
// Other PostHTML plugins first:
require("posthtml-expressions")({ locals: { title: "PkgPulse" } }),
require("posthtml-include")(),
// Then minify:
htmlnano({
removeComments: "safe",
collapseWhitespace: "conservative",
minifyCss: true,
}),
]).process(html)
console.log(result.html)
With Parcel
// .htmlnanorc.js (Parcel uses htmlnano automatically):
module.exports = {
removeComments: "safe",
collapseWhitespace: "conservative",
minifyCss: true,
minifyJs: true,
minifySvg: true,
removeRedundantAttributes: true,
}
// Parcel applies htmlnano during production builds automatically
Available modules
htmlnano modules:
Whitespace:
collapseWhitespace: "conservative" | "all" | false
collapseAttributeWhitespace: true/false
Comments:
removeComments: "safe" | "all" | false
Attributes:
removeRedundantAttributes: true/false
removeEmptyAttributes: true/false
collapseBooleanAttributes: true/false
deduplicateAttributeValues: true/false
removeOptionalTags: true/false
normalizeAttributeValues: true/false
Inline code:
minifyCss: true/false (uses cssnano)
minifyJs: true/false (uses terser)
minifySvg: true/false (uses svgo)
minifyJson: true/false
Other:
mergeStyles: true/false
mergeScripts: true/false
sortAttributes: true/false
sortAttributesWithLists: true/false
removeUnusedCss: true/false (uses uncss)
minify-html
minify-html — Rust-based HTML minifier:
Node.js usage
import { minify } from "@nicolo-ribaudo/minify-html"
const html = Buffer.from(`
<!DOCTYPE html>
<html>
<head>
<title>PkgPulse</title>
</head>
<body>
<!-- Comment -->
<div class="container">
<h1>Compare packages</h1>
</div>
</body>
</html>
`)
const minified = minify(html, {
do_not_minify_doctype: false,
ensure_spec_compliant_unquoted_attribute_values: false,
keep_closing_tags: false,
keep_html_and_head_opening_tags: false,
keep_spaces_between_attributes: false,
keep_comments: false,
minify_css: true,
minify_js: true,
remove_bangs: false,
remove_processing_instructions: false,
})
console.log(minified.toString())
// → Very aggressively minified output
Configuration options
import { minify } from "@nicolo-ribaudo/minify-html"
const result = minify(htmlBuffer, {
// Keep certain elements:
keep_closing_tags: true, // Preserve </p>, </li>, etc.
keep_html_and_head_opening_tags: true,
keep_comments: false,
keep_spaces_between_attributes: false,
// Inline minification:
minify_css: true, // Minify <style> and style attributes
minify_js: true, // Minify <script> content
// Safety:
do_not_minify_doctype: true,
ensure_spec_compliant_unquoted_attribute_values: true,
})
Performance comparison
Benchmark: Minifying a 100KB HTML file (average of 100 runs)
html-minifier-terser: 45ms
htmlnano (safe): 38ms
htmlnano (max): 52ms
minify-html: 4ms ← ~10x faster
For large-scale static sites (1000+ pages):
html-minifier-terser: ~45 seconds
minify-html: ~4 seconds
minify-html is written in Rust and compiled to native code,
making it significantly faster for batch processing.
Build tool integration
// Vite plugin:
import { minify } from "@nicolo-ribaudo/minify-html"
import { Plugin } from "vite"
function htmlMinifyPlugin(): Plugin {
return {
name: "html-minify",
enforce: "post",
apply: "build",
transformIndexHtml(html) {
return minify(Buffer.from(html), {
minify_css: true,
minify_js: true,
keep_closing_tags: false,
}).toString()
},
}
}
// vite.config.ts:
export default {
plugins: [htmlMinifyPlugin()],
}
Feature Comparison
| Feature | html-minifier-terser | htmlnano | minify-html |
|---|---|---|---|
| Language | JavaScript | JavaScript | Rust (WASM/native) |
| Speed | Medium | Medium | Fast (10x) |
| Configurability | High (30+ options) | High (modular) | Medium |
| Inline CSS minify | ✅ (clean-css) | ✅ (cssnano) | ✅ (built-in) |
| Inline JS minify | ✅ (terser) | ✅ (terser) | ✅ (built-in) |
| SVG minification | ❌ | ✅ (svgo) | ❌ |
| PostHTML integration | ❌ | ✅ | ❌ |
| Plugin system | ❌ | ✅ | ❌ |
| Used by | Webpack, Vite plugins | Parcel | Custom builds |
| WASM support | N/A | N/A | ✅ |
| Weekly downloads | ~10M | ~2M | ~500K |
When to Use Each
Use html-minifier-terser if:
- Need the most battle-tested HTML minifier
- Want fine-grained control over every optimization
- Using Webpack or html-webpack-plugin
- Need to minify inline CSS/JS with terser/clean-css
Use htmlnano if:
- Using PostHTML for HTML processing
- Using Parcel (built-in)
- Want modular, plugin-based minification
- Need SVG minification in inline SVGs
Use minify-html if:
- Need maximum performance (large sites, many pages)
- Building a static site generator or custom build tool
- Want Rust-native speed for batch processing
- Fine with fewer configuration options for faster output
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on html-minifier-terser v7.x, htmlnano v2.x, and @nicolo-ribaudo/minify-html v0.15.x.
Compare build tooling and performance utilities on PkgPulse →