<!-- PkgPulse AI-readable guide source -->
<!-- Canonical: https://www.pkgpulse.com/guides/html-minifier-terser-vs-htmlnano-vs-minify-html-html-2026 -->
<!-- Raw Markdown: https://www.pkgpulse.com/guides/html-minifier-terser-vs-htmlnano-vs-minify-html-html-2026/raw.md -->
<!-- Source path: content/guides/html-minifier-terser-vs-htmlnano-vs-minify-html-html-2026.mdx -->

---
og_image: "/images/guides/html-minifier-terser-vs-htmlnano-vs-minify-html-html-2026.webp"
title: "html-minifier-terser vs htmlnano vs minify-html (2026)"
description: "Compare html-minifier-terser, htmlnano, and minify-html for minifying HTML in Node.js. Template minification, build tool integration, performance benchmarks."
date: "2026-03-09"
author: "PkgPulse Team"
tags: ["nodejs", "typescript", "developer-tools", "performance"]
---

## 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](https://github.com/terser/html-minifier-terser) — feature-rich HTML minifier:

### Basic usage

```typescript
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

```typescript
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

```typescript
// 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](https://github.com/posthtml/htmlnano) — modular HTML minifier:

### Basic usage

```typescript
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

```typescript
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

```typescript
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

```json
// .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](https://github.com/nicolo-ribaudo/minify-html) — Rust-based HTML minifier:

### Node.js usage

```typescript
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

```typescript
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

```typescript
// 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.

## Inline CSS and JavaScript Minification Differences

One of the most impactful — and risky — features of HTML minifiers is their ability to process inline `<style>` blocks and `<script>` tags as part of the HTML minification pass. All three libraries support this, but they use different underlying tools and have different safety guarantees.

`html-minifier-terser` delegates inline CSS to `clean-css` and inline JavaScript to `terser` — the same `terser` used for standalone JavaScript minification. This means you get production-grade JS minification inside your HTML: dead code elimination, constant folding, variable name mangling. The downside is that mangling variable names in inline scripts can break code that relies on global variable names being preserved. The `minifyJS: { mangle: false }` option disables name mangling if you need safety over compression.

`htmlnano` uses `cssnano` for CSS (which is the PostCSS-based CSS minifier, offering more PostCSS-aware transformations than clean-css) and also delegates to `terser` for JavaScript. The key difference is that htmlnano's plugin architecture lets you swap out the underlying tools — you can configure `cssnano` options directly through htmlnano's `minifyCss` option, giving you access to cssnano's full preset system. The `minifySvg` module (using svgo) is unique to htmlnano and is meaningful for pages with inline SVG icons or illustrations, which can often be reduced by 30-50% in size.

`minify-html` handles CSS and JS minification internally with its own Rust implementation rather than delegating to external tools. This makes it faster but less configurable — you cannot pass custom terser options or use cssnano presets. The tradeoff is predictability: minify-html's built-in minifier is more conservative by default, reducing the risk of broken inline scripts at the cost of slightly less aggressive compression.

## Static Site Generator Integration Patterns

For static site generators (SSGs), HTML minification sits at the end of the build pipeline and processes every generated HTML file. The choice of minifier affects both build time and the risk of broken output pages, making it a higher-stakes decision than for single-page apps.

Next.js, Nuxt, and Astro do not expose HTML minifier configuration directly — they handle it internally. Next.js uses `html-minifier-terser` internally (configurable via `compress` options in `next.config.js`). Astro compiles and minifies HTML as part of its Vite build. For custom SSGs or build scripts using tools like Eleventy, custom Vite plugins, or Metalsmith, you control the minifier choice directly.

For Eleventy specifically, `html-minifier-terser` is the most commonly documented choice and is used in Eleventy's official starter templates. A common pattern is an Eleventy transform that runs `html-minifier-terser` on every HTML output file during production builds, skipping minification in development for faster reloads. `minify-html` is an increasingly popular alternative for large Eleventy sites with hundreds of pages, where the 10x speed difference measurably shortens build times. The key consideration is testing the output: minify-html's aggressive removal of optional closing tags and unquoted attributes can reveal HTML that depends on lenient browser parsing, making a thorough test pass essential before deploying to production.

*[Compare build tooling and performance utilities on PkgPulse →](https://www.pkgpulse.com)*

## Measuring Actual Savings and Avoiding Over-Minification

Before committing to an HTML minifier in a build pipeline, it's worth measuring what savings you're actually getting — and whether aggressive minification introduces rendering bugs. HTML minification savings are not uniform: a marketing page with heavy inline CSS will see much better compression ratios than a data-heavy table-rendered page with minimal markup.

The most reliable way to measure minification impact is to run your minifier against a sample of representative HTML files and compare sizes before and after, then compare against your gzip/Brotli-compressed output. The key insight is that HTML minification and content encoding are not independent: gzip and Brotli use LZ-based compression that is very effective at compressing repetitive whitespace patterns, so the whitespace that HTML minifiers remove often compresses well already. Real-world measurements show that HTML minification (whitespace removal only) typically reduces gzip-compressed HTML by 3-8%, while removing whitespace plus collapsing attributes and removing optional tags adds another 2-5% on top of that.

Where HTML minification provides more meaningful gains is for HTML that is served without content encoding — for example, server-side rendered HTML served from edge functions or CDN edges that don't apply Brotli compression, or HTML embedded in JSON API responses as string values (common in some headless CMS architectures).

Testing minified output for rendering correctness is non-negotiable before deploying to production. The most common regressions come from three sources: inline JavaScript that relies on preserved whitespace in string literals (minifying `<script>var x = " hello "` can alter the string content), CSS that uses whitespace-sensitive properties like `white-space: pre` in combination with dynamically generated content, and conditional HTML comments (`<!--[if IE]>`) that should be preserved but get removed by `removeComments: true`. Run your full test suite against minified output in CI rather than only testing against unminified development builds.

## When to Use Each

**Use html-minifier-terser if:**
- You are minifying HTML in a Node.js build script or CLI pipeline
- You want fine-grained control: remove specific whitespace, inline JS/CSS, collapse attributes
- You need a widely-adopted package with extensive configuration options and community knowledge

**Use htmlnano if:**
- You are using PostHTML as your HTML transformation pipeline
- You want modular minification where each optimization is a separate PostHTML plugin
- You need to selectively enable/disable specific minification transforms

**Use minify-html (vise/minify-html) if:**
- Performance is critical — it is 40-100× faster than html-minifier-terser on large HTML files
- You are processing many large HTML files in CI (static site generation)
- You have Rust or WASM in your toolchain (it ships as a compiled binary or WASM module)

In 2026, the right choice is contextual. For most webpack/Vite build pipelines, html-minifier-terser remains the default via `html-webpack-plugin` and similar tools. For custom build pipelines or static site generators processing many files, minify-html's performance advantage justifies the additional dependency complexity.

A practical benchmark note: HTML minification savings vary significantly by content type. For content-heavy HTML (blog posts, documentation), savings are typically 5-15%. For attribute-heavy component templates, savings can reach 20-30%. Run minification benchmarks on your actual HTML before assuming a specific savings target.

## Methodology

Download data from npm registry (weekly average, February 2026). Feature comparison based on html-minifier-terser v5.x, htmlnano v2.x, and minify-html v0.15.x. html-minifier-terser is a maintained fork of the unmaintained html-minifier, created to keep security patches current. htmlnano is the HTML minification step in PostHTML pipelines and is used by Parcel's HTML optimizer. minify-html is a Rust-based binary with Node.js WASM and native addon bindings — it achieves its performance advantage by avoiding JavaScript regex operations entirely and using a hand-written parser in Rust.


*See also: [cac vs meow vs arg 2026](/guides/cac-vs-meow-vs-arg-lightweight-cli-argument-parsers-2026) and [Ink vs @clack/prompts vs Enquirer](/guides/ink-vs-clack-vs-enquirer-interactive-cli-nodejs-2026), [acorn vs @babel/parser vs espree](/guides/acorn-vs-babel-parser-vs-espree-javascript-ast-parsers-2026).*
