Skip to content

Highly configurable, well-tested, JavaScript-based HTML minifier (maintained fork of html-minifier-terser and html-minifier)

License

Notifications You must be signed in to change notification settings

j9t/html-minifier-next

Repository files navigation

HTML Minifier Next

npm version Build status

HTML Minifier Next (HMN) is a highly configurable, well-tested, JavaScript-based HTML minifier.

The project has been based on Terser’s html-minifier-terser, which in turn had been based on Juriy Zaytsev’s html-minifier (HMN offers additional features, but is compatible with both). It was set up because as of 2025, both html-minifier-terser and html-minifier have been unmaintained for some time. As the project seems maintainable [to me, Jens]—even more so with community support—, it’s being updated and documented further in this place.

Installation

From npm for use as a command line app:

npm i -g html-minifier-next

From npm for programmatic use:

npm i html-minifier-next

General usage

CLI

Use html-minifier-next --help to check all available options:

Option Description Example
--input-dir <dir> Specify an input directory (best restricted with --file-ext) --input-dir=src
--output-dir <dir> Specify an output directory --output-dir=dist
--file-ext <extensions> Specify file extension(s) to process (overrides config file setting) --file-ext=html, --file-ext=html,htm,php, --file-ext="html, htm, php"
-o <file>, --output <file> Specify output file (reads from file arguments or STDIN) File to file: html-minifier-next input.html -o output.html
Pipe to file: cat input.html | html-minifier-next -o output.html
File to STDOUT: html-minifier-next input.html
-c <file>, --config-file <file> Use a configuration file --config-file=html-minifier.json
-v, --verbose Show detailed processing information (active options, file statistics) html-minifier-next --input-dir=src --output-dir=dist --verbose --collapse-whitespace
-d, --dry Dry run: Process and report statistics without writing output html-minifier-next input.html --dry --collapse-whitespace
-V, --version Output the version number html-minifier-next --version

Configuration file

You can also use a configuration file to specify options. The file can be either JSON format or a JavaScript module that exports the configuration object:

JSON configuration example:

{
  "collapseWhitespace": true,
  "removeComments": true,
  "fileExt": "html,htm"
}

JavaScript module configuration example:

module.exports = {
  collapseWhitespace: true,
  removeComments: true,
  fileExt: "html,htm"
};

Using a configuration file:

# Specify config file
html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir=dist

# CLI arguments override config file settings
html-minifier-next --config-file=html-minifier.json --file-ext=xml --input-dir=src --output-dir=dist

Node.js

ESM with Node.js ≥16.14:

import { minify } from 'html-minifier-next';

const result = await minify('<p title="blah" id="moo">foo</p>', {
  removeAttributeQuotes: true,
});
console.log(result); // “<p title=blah id=moo>foo</p>”

CommonJS:

const { minify } = require('html-minifier-next');

(async () => {
  const result = await minify('<p title="blah" id="moo">foo</p>', {
    removeAttributeQuotes: true,
  });
  console.log(result);
})();

See the original blog post for details of how it works, description of each option, testing results, and conclusions.

For lint-like capabilities, take a look at HTMLLint.

Options quick reference

Most of the options are disabled by default. Experiment and find what works best for you and your project.

Options can be used in config files (camelCase) or via CLI flags (kebab-case with -- prefix). Options that default to true use --no- prefix in CLI to disable them.

Option (config/CLI) Description Default
caseSensitive
--case-sensitive
Treat attributes in case-sensitive manner (useful for custom HTML elements) false
collapseBooleanAttributes
--collapse-boolean-attributes
Omit attribute values from boolean attributes false
collapseInlineTagWhitespace
--collapse-inline-tag-whitespace
Don’t leave any spaces between display: inline; elements when collapsing—use with collapseWhitespace: true false
collapseWhitespace
--collapse-whitespace
Collapse whitespace that contributes to text nodes in a document tree false
conservativeCollapse
--conservative-collapse
Always collapse to 1 space (never remove it entirely)—use with collapseWhitespace: true false
continueOnParseError
--continue-on-parse-error
Handle parse errors instead of aborting false
customAttrAssign
--custom-attr-assign
Arrays of regexes that allow to support custom attribute assign expressions (e.g., <div flex?="{{mode != cover}}"></div>) []
customAttrCollapse
--custom-attr-collapse
Regex that specifies custom attribute to strip newlines from (e.g., /ng-class/)
customAttrSurround
--custom-attr-surround
Arrays of regexes that allow to support custom attribute surround expressions (e.g., <input {{#if value}}checked="checked"{{/if}}>) []
customEventAttributes
--custom-event-attributes
Arrays of regexes that allow to support custom event attributes for minifyJS (e.g., ng-click) [ /^on[a-z]{3,}$/ ]
customFragmentQuantifierLimit
--custom-fragment-quantifier-limit
Set maximum quantifier limit for custom fragments to prevent ReDoS attacks 200
decodeEntities
--decode-entities
Use direct Unicode characters whenever possible false
html5
--no-html5
Parse input according to the HTML specification true
ignoreCustomComments
--ignore-custom-comments
Array of regexes that allow to ignore certain comments, when matched [ /^!/, /^\s*#/ ]
ignoreCustomFragments
--ignore-custom-fragments
Array of regexes that allow to ignore certain fragments, when matched (e.g., <?php … ?>, {{ … }}, etc.) [ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]
includeAutoGeneratedTags
--no-include-auto-generated-tags
Insert elements generated by HTML parser true
inlineCustomElements
--inline-custom-elements
Array of names of custom elements which are inline []
keepClosingSlash
--keep-closing-slash
Keep the trailing slash on void elements false
maxInputLength
--max-input-length
Maximum input length to prevent ReDoS attacks (disabled by default) undefined
maxLineLength
--max-line-length
Specify a maximum line length; compressed output will be split by newlines at valid HTML split-points
minifyCSS
--minify-css
Minify CSS in style elements and style attributes (uses clean-css) false (could be true, Object, Function(text, type))
minifyJS
--minify-js
Minify JavaScript in script elements and event attributes (uses Terser) false (could be true, Object, Function(text, inline))
minifyURLs
--minify-urls
Minify URLs in various attributes (uses relateurl) false (could be String, Object, Function(text), async Function(text))
noNewlinesBeforeTagClose
--no-newlines-before-tag-close
Never add a newline before a tag that closes an element false
preserveLineBreaks
--preserve-line-breaks
Always collapse to 1 line break (never remove it entirely) when whitespace between tags includes a line break—use with collapseWhitespace: true false
preventAttributesEscaping
--prevent-attributes-escaping
Prevents the escaping of the values of attributes false
processConditionalComments
--process-conditional-comments
Process contents of conditional comments through minifier false
processScripts
--process-scripts
Array of strings corresponding to types of script elements to process through minifier (e.g., text/ng-template, text/x-handlebars-template, etc.) []
quoteCharacter
--quote-character
Type of quote to use for attribute values (' or ")
removeAttributeQuotes
--remove-attribute-quotes
Remove quotes around attributes when possible false
removeComments
--remove-comments
Strip HTML comments false
removeEmptyAttributes
--remove-empty-attributes
Remove all attributes with whitespace-only values false (could be true, Function(attrName, tag))
removeEmptyElements
--remove-empty-elements
Remove all elements with empty contents false
removeOptionalTags
--remove-optional-tags
Remove optional tags false
removeRedundantAttributes
--remove-redundant-attributes
Remove attributes when value matches default false
removeScriptTypeAttributes
--remove-script-type-attributes
Remove type="text/javascript" from script elements; other type attribute values are left intact false
removeStyleLinkTypeAttributes
--remove-style-link-type-attributes
Remove type="text/css" from style and link elements; other type attribute values are left intact false
removeTagWhitespace
--remove-tag-whitespace
Remove space between attributes whenever possible; note that this will result in invalid HTML false
sortAttributes
--sort-attributes
Sort attributes by frequency false
sortClassName
--sort-class-name
Sort style classes by frequency false
trimCustomFragments
--trim-custom-fragments
Trim whitespace around ignoreCustomFragments false
useShortDoctype
--use-short-doctype
Replaces the doctype with the short (HTML) doctype false

Sorting attributes and style classes

Minifier options like sortAttributes and sortClassName won’t impact the plain‑text size of the output. However, using these options for more consistent ordering improves the compression ratio for gzip and Brotli used over HTTP.

Minification comparison

How does HTML Minifier Next compare to other solutions, like minimize, htmlcompressor.com, htmlnano, and minify-html? (All with the most aggressive settings, but without hyper-optimization.)

Site Original Size (KB) HTML Minifier Next minimize html­compressor.com htmlnano minify-html
A List Apart 62 53 58 56 54 55
Amazon 206 195 203 200 196 n/a
Apple 183 143 169 167 160 166
BBC 689 633 683 n/a 646 648
CSS-Tricks 163 121 149 146 127 145
ECMAScript 7233 6338 6610 n/a 6557 6563
EFF 57 48 52 52 51 50
FAZ 1871 1747 1785 n/a 1647 n/a
Frontend Dogma 119 114 128 119 126 119
Google 18 16 18 18 17 n/a
Ground News 3308 2946 3295 n/a 3042 n/a
HTML 149 147 155 148 153 149
Leanpub 1279 1063 1274 n/a 1070 n/a
Mastodon 35 26 34 34 30 33
MDN 107 61 67 67 63 n/a
Middle East Eye 224 197 204 205 204 202
SitePoint 485 354 481 n/a 423 465
United Nations 151 114 130 123 121 124
W3C 50 36 41 39 38 39

Examples

CLI

Sample command line:

html-minifier-next --collapse-whitespace --remove-comments --minify-js true --input-dir=. --output-dir=example

Process specific files and directories:

# Process only HTML files
html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --file-ext=html

# Process multiple file extensions
html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --file-ext=html,htm,php

# Using configuration file that sets `fileExt` (e.g., `"fileExt": "html,htm"`)
html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir=dist

# Process all files (default behavior)
html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist
# When processing all files, non-HTML files will also be read as UTF‑8 and passed to the minifier
# Consider restricting with `--file-ext` to avoid touching binaries (e.g., images, archives)

Dry run mode (preview outcome without writing files):

# Preview with output file
html-minifier-next input.html -o output.html --dry --collapse-whitespace

# Preview directory processing with statistics per file and total
html-minifier-next --input-dir=src --output-dir=dist --dry --collapse-whitespace
# Output: [DRY RUN] Would process directory: src → dist
#   index.html: 1,234 → 892 bytes (-342, 27.7%)
#   about.html: 2,100 → 1,654 bytes (-446, 21.2%)
# ---
# Total: 3,334 → 2,546 bytes (-788, 23.6%)

Verbose mode (show detailed processing information):

# Show processing details while minifying
html-minifier-next --input-dir=src --output-dir=dist --verbose --collapse-whitespace
# Output: Options: collapseWhitespace, html5, includeAutoGeneratedTags
#   ✓ src/index.html: 1,234 → 892 bytes (-342, 27.7%)
#   ✓ src/about.html: 2,100 → 1,654 bytes (-446, 21.2%)
# ---
# Total: 3,334 → 2,546 bytes (-788, 23.6%)

# `--dry` automatically enables verbose output
html-minifier-next --input-dir=src --output-dir=dist --dry --collapse-whitespace

Special cases

Ignoring chunks of markup

If you have chunks of markup you would like preserved, you can wrap them with <!-- htmlmin:ignore -->.

Minifying JSON-LD

You can minify script elements with JSON-LD by setting { processScripts: ['application/ld+json'] }. Note that this minification is rudimentary; it’s mainly useful for removing newlines and excessive whitespace.

Preserving SVG elements

SVG elements are automatically recognized, and when they are minified, both case-sensitivity and closing-slashes are preserved, regardless of the minification settings used for the rest of the file.

Working with invalid markup

HTML Minifier Next can’t work with invalid or partial chunks of markup. This is because it parses markup into a tree structure, then modifies it (removing anything that was specified for removal, ignoring anything that was specified to be ignored, etc.), then it creates a markup out of that tree and returns it.

Input markup (e.g., <p id="">foo) → Internal representation of markup in a form of tree (e.g., { tag: "p", attr: "id", children: ["foo"] }) → Transformation of internal representation (e.g., removal of id attribute) → Output of resulting markup (e.g., <p>foo</p>)

HMN can’t know that the original markup represented only part of the tree. It parses a complete tree and, in doing so, loses information about the input being malformed or partial. As a result, it can’t emit a partial or malformed tree.

To validate HTML markup, use the W3C validator or one of several validator packages.

Security

ReDoS protection

This minifier includes protection against regular expression denial of service (ReDoS) attacks:

  • Custom fragment quantifier limits: The customFragmentQuantifierLimit option (default: 200) prevents exponential backtracking by replacing unlimited quantifiers (*, +) with bounded ones in regular expressions.

  • Input length limits: The maxInputLength option allows you to set a maximum input size to prevent processing of excessively large inputs that could cause performance issues.

  • Enhanced pattern detection: The minifier detects and warns about various ReDoS-prone patterns including nested quantifiers, alternation with quantifiers, and multiple unlimited quantifiers.

Important: When using custom ignoreCustomFragments, ensure your regular expressions don’t contain unlimited quantifiers (*, +) without bounds, as these can lead to ReDoS vulnerabilities.

Custom fragment examples

Safe patterns (recommended):

ignoreCustomFragments: [
  /<%[\s\S]{0,1000}?%>/,         // JSP/ASP with explicit bounds
  /<\?php[\s\S]{0,5000}?\?>/,    // PHP with bounds
  /\{\{[^}]{0,500}\}\}/          // Handlebars without nested braces
]

Potentially unsafe patterns (will trigger warnings):

ignoreCustomFragments: [
  /<%[\s\S]*?%>/,                // Unlimited quantifiers
  /<!--[\s\S]*?-->/,             // Could cause issues with very long comments
  /\{\{.*?\}\}/,                 // Nested unlimited quantifiers
  /(script|style)[\s\S]*?/       // Multiple unlimited quantifiers
]

Template engine configurations:

// Handlebars/Mustache
ignoreCustomFragments: [/\{\{[\s\S]{0,1000}?\}\}/]

// Liquid (Jekyll)
ignoreCustomFragments: [/\{%[\s\S]{0,500}?%\}/, /\{\{[\s\S]{0,500}?\}\}/]

// Angular
ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/]

// Vue.js
ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/]

Important: When using custom ignoreCustomFragments, the minifier automatically applies bounded quantifiers to prevent ReDoS attacks, but you can also write safer patterns yourself using explicit bounds.

Running HTML Minifier Next

Benchmarks

Benchmarks for minified HTML:

cd benchmarks
npm install
npm run benchmarks

Local server

npm run serve

Acknowledgements

With many thanks to all the previous authors of HTML Minifier, especially Juriy Zaytsev, and to everyone who helped make this new edition better, particularly Daniel Ruf and Jonas Geiler.

About

Highly configurable, well-tested, JavaScript-based HTML minifier (maintained fork of html-minifier-terser and html-minifier)

Resources

License

Security policy

Stars

Watchers

Forks

Sponsor this project