Skip to main content
Version: 2.0.0

Modules

By default the modules should only perform safe transforms, see the module documentation below for details. You can disable modules by passing false as option, and enable them by passing true.

collapseAttributeWhitespace

Collapse redundant white spaces in list-like attributes (class, rel, ping).

Example

Source:

<a class=" content  page  " style="  display: block;    " href="   https://example.com"></a>

Minified:

<a class="content page" style="display: block;" href="https://example.com"></a>

collapseWhitespace

Collapses redundant white spaces (including new lines). It doesn’t affect white spaces in the elements <style>, <textarea>, <script> and <pre>.

Options

  • conservative — collapses all redundant white spaces to 1 space (default)
  • aggressive — collapses all whitespaces that are redundant and safe to remove
  • all — collapses all redundant white spaces

Side effects

all <i>hello</i> <i>world</i> or <i>hello</i><br><i>world</i> after minification will be rendered as helloworld. To prevent that use either the default conservative option, or the aggressive option.

Example

Source:

<div>
hello world!
<a href="#">answer</a>
<style>div { color: red; } </style>
<main></main>
</div>

Minified (with all):

<div>hello world!<a href="#">answer</a><style>div  { color: red; }  </style><main></main></div>

Minified (with aggressive):

<div> hello world! <a href="#">answer</a> <style>div  { color: red; }  </style><main></main></div>

Minified (with conservative):

<div> hello world! <a href="#">answer</a> <style>div  { color: red; }  </style> <main></main> </div>

deduplicateAttributeValues

Remove duplicate values from list-like attributes (class, rel, ping).

Example

Source:

<div class="sidebar left sidebar"></div>

Minified:

<div class="sidebar left"></div>

removeComments

Options

  • safe – removes all HTML comments except the conditional comments and <!--noindex--><!--/noindex--> (default)
  • all — removes all HTML comments
  • A RegExp — only HTML comments matching the given regexp will be removed.
  • A Function that returns boolean — removes HTML comments that can make the given callback function returns truthy value.

Example

Source:

{
removeComments: 'all'
}
<div><!-- test --></div>

Minified:

<div></div>

Source:

{
removeComments: /<!--(\/)?noindex-->/
}
<div><!--noindex-->this text will not be indexed<!--/noindex-->Lorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>

Minified:

<div>this text will not be indexedLorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>

Source:

{
removeComments: (comments) => {
if (comments.includes('noindex')) return true;
return false;
}
}
<div><!--noindex-->this text will not be indexed<!--/noindex-->Lorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>

Minified:

<div>this text will not be indexedLorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>

removeEmptyAttributes

Removes empty safe-to-remove attributes.

Side effects

This module could break your styles or JS if you use selectors with attributes:

img[style=""] {
margin: 10px;
}

Example

Source:

<img src="foo.jpg" alt="" style="">

Minified:

<img src="foo.jpg" alt="">

removeAttributeQuotes

Remove quotes around attributes when possible, see HTML Standard - 12.1.2.3 Attributes - Unquoted attribute value syntax.

Example

Source:

<div class="foo" title="hello world"></div>

Minified:

<div class=foo title="hello world"></div>

Notice

The feature is implemented by posthtml-render's quoteAllAttributes, which is one of the PostHTML's option. So removeAttributeQuotes could be overriden by other PostHTML's plugins and PostHTML's configuration.

For example:

posthtml([
htmlnano({
removeAttributeQuotes: true
})
]).process(html, {
quoteAllAttributes: true
})

removeAttributeQuotes will not work because PostHTML's quoteAllAttributes takes the priority.

removeUnusedCss

Removes unused CSS inside <style> tags with either uncss or PurgeCSS.

With uncss

You have to install uncss in order to use this feature:

npm install --save-dev uncss
# if you prefer yarn
# yarn add --dev uncss
# if you prefer pnpm
# pnpm install --save-dev uncss

You can also use a mainted fork @novaatwarren/uncss instead.

Options

See the documentation of uncss for all supported options.

uncss options can be passed directly to the removeUnusedCss module:

htmlnano.process(html, {
removeUnusedCss: {
ignore: ['.do-not-remove']
}
});

The following uncss options are ignored if passed to the module:

  • stylesheets
  • ignoreSheets
  • raw

With PurgeCSS

Use PurgeCSS instead of uncss by adding tool: 'purgeCSS' to the options.

You have to install purgecss in order to use this feature:

npm install --save-dev purgecss
# if you prefer yarn
# yarn add --dev purgecss
# if you prefer pnpm
# pnpm install --save-dev purgecss
Options

See the documentation of PurgeCSS for all supported options.

PurgeCSS options can be passed directly to the removeUnusedCss module:

htmlnano.process(html, {
removeUnusedCss: {
tool: 'purgeCSS',
safelist: ['.do-not-remove']
}
});

The following PurgeCSS options are ignored if passed to the module:

  • content
  • css
  • extractors

Example

Source:

<div class="b">
<style>
.a {
margin: 10px 10px 10px 10px;
}
.b {
color: #ff0000;
}
</style>
</div>

Optimized:

<div class="b">
<style>
.b {
color: #ff0000;
}
</style>
</div>

minifyCss

Minifies CSS with cssnano inside <style> tags and style attributes.

You have to install cssnano and postcss in order to use this feature:

npm install --save-dev cssnano postcss
# if you prefer yarn
# yarn add --dev cssnano postcss
# if you prefer pnpm
# pnpm install --save-dev cssnano postcss

Options

See the documentation of cssnano for all supported optimizations. By default CSS is minified with preset default, which shouldn't have any side-effects.

To use another preset or disabled some optimizations pass options to minifyCss module:

htmlnano.process(html, {
minifyCss: {
preset: ['default', {
discardComments: {
removeAll: true,
},
}]
}
});

Example

Source:

<div>
<style>
h1 {
margin: 10px 10px 10px 10px;
color: #ff0000;
}
</style>
</div>

Minified:

<div>
<style>h1{margin:10px;color:red}</style>
</div>

minifyJs

Minifies JS using Terser inside <script> tags.

You have to install terser in order to use this feature:

npm install --save-dev terser
# if you prefer yarn
# yarn add --dev terser
# if you prefer pnpm
# pnpm install --save-dev terser

Options

See the documentation of Terser for all supported options. Terser options can be passed directly to the minifyJs module:

htmlnano.process(html, {
minifyJs: {
output: { quote_style: 1 },
},
});

Example

Source:

<div>
<script>
/* comment */
const foo = function () {

};
</script>
</div>

Minified:

<div>
<script>const foo=function(){};</script>
</div>

minifyJson

Minifies JSON inside <script type="application/json"></script>.

Example

Source:

<script type="application/json">
{
"user": "me"
}
</script>

Minified:

<script type="application/json">{"user":"me"}</script>

minifySvg

Minifies SVG inside <svg> tags using SVGO.

Options

See the documentation of SVGO for all supported options. SVGO options can be passed directly to the minifySvg module:

htmlnano.process(html, {
minifySvg: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
builtinPluginName: {
optionName: 'optionValue'
},
},
},
}
]
}
});

Example

Source:

<svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="red" />

<circle cx="150" cy="100" r="80" fill="green" />

<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>
</svg>`

Minified:

<svg baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="red"/><circle cx="150" cy="100" r="80" fill="green"/><text x="150" y="125" font-size="60" text-anchor="middle" fill="#fff">SVG</text></svg>

minifyConditionalComments

Minify content inside conditional comments.

Example

Source:

<!--[if lte IE 7]>
<style type="text/css">
.title {
color: red;
}
</style>
<![endif]-->

Minified:

<!--[if lte IE 7]><style>.title{color:red}</style><![endif]-->

removeRedundantAttributes

Removes redundant attributes from tags if they contain default values:

  • method="get" from <form>
  • type="text" from <input>
  • type="submit" from <button>
  • language="javascript" and type="text/javascript" from <script>
  • charset from <script> if it's an external script
  • media="all" from <style> and <link>
  • type="text/css" from <link rel="stylesheet">

Options

This module is disabled by default, change option to true to enable this module.

Side effects

This module could break your styles or JS if you use selectors with attributes:

form[method="get"] {
color: red;
}

Example

Source:

<form method="get">
<input type="text">
</form>

Minified:

<form>
<input>
</form>

collapseBooleanAttributes

Collapses boolean attributes (like disabled) to the minimized form.

Options

If your document uses AMP, set the amphtml flag to collapse additonal, AMP-specific boolean attributes:

"collapseBooleanAttributes": {
"amphtml": true
}

Side effects

This module could break your styles or JS if you use selectors with attributes:

button[disabled="disabled"] {
color: red;
}

Example

Source:

<button disabled="disabled">click</button>
<script defer=""></script>

Minified:

<button disabled>click</button>
<script defer></script>

mergeStyles

Merges multiple <style> with the same media and type into one tag. <style scoped>...</style> are skipped.

Example

Source:

<style>h1 { color: red }</style>
<style media="print">div { color: blue }</style>

<style type="text/css" media="print">a {}</style>
<style>div { font-size: 20px }</style>

Minified:

<style>h1 { color: red } div { font-size: 20px }</style>
<style media="print">div { color: blue } a {}</style>

mergeScripts

Merge multiple <script> with the same attributes (id, class, type, async, defer) into one (last) tag.

Side effects

It could break your code if the tags with different attributes share the same variable scope. See the example below.

Example

Source:

<script>const foo = 'A:1';</script>
<script class="test">foo = 'B:1';</script>
<script type="text/javascript">foo = 'A:2';</script>
<script defer>foo = 'C:1';</script>
<script>foo = 'A:3';</script>
<script defer="defer">foo = 'C:2';</script>
<script class="test" type="text/javascript">foo = 'B:2';</script>

Minified:

<script>const foo = 'A:1'; foo = 'A:2'; foo = 'A:3';</script>
<script defer="defer">foo = 'C:1'; foo = 'C:2';</script>
<script class="test" type="text/javascript">foo = 'B:1'; foo = 'B:2';</script>

custom

It's also possible to pass custom modules in the minifier. As a function:

const options = {
custom: function (tree, options) {
// Some minification
return tree;
}
};

Or as a list of functions:

const options = {
custom: [
function (tree, options) {
// Some minification
return tree;
},

function (tree, options) {
// Some other minification
return tree;
}
]
};

options is an object with all options that were passed to the plugin.

sortAttributesWithLists

Sort values in list-like attributes (class, rel, ping).

The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression.

Options

  • alphabetical: Default option. Sort attribute values in alphabetical order.
  • frequency: Sort attribute values by frequency.

Example

alphabetical

Source:

<div class="foo baz bar">click</div>

Processed:

<div class="bar baz foo">click</div>

frequency

Source:

<div class="foo baz bar"></div><div class="bar foo"></div>

Processed:

<div class="foo bar baz"></div><div class="foo bar"></div>

sortAttributes

Sort attributes inside elements.

The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression.

Options

  • alphabetical: Default option. Sort attributes in alphabetical order.
  • frequency: Sort attributes by frequency.

Example

alphabetical

Source:

<input type="text" class="form-control" name="testInput" autofocus="" autocomplete="off" id="testId">

Processed:

<input autocomplete="off" autofocus="" class="form-control" id="testId" name="testInput" type="text">

frequency

Source:

<input type="text" class="form-control" name="testInput" id="testId">
<a id="testId" href="#" class="testClass"></a>
<img width="20" src="../images/image.png" height="40" alt="image" class="cls" id="id2">

Processed:

<input class="form-control" id="testId" type="text" name="testInput">
<a class="testClass" id="testId" href="#"></a>
<img class="cls" id="id2" width="20" src="../images/image.png" height="40" alt="image">

minifyUrls

Convert absolute URL to relative URL using relateurl.

You have to install relateurl, terser and srcset in order to use this feature:

npm install --save-dev relateurl terser srcset
# if you prefer yarn
# yarn add --dev relateurl terser srcset
# if you prefer pnpm
# pnpm install --save-dev relateurl terser srcset

Options

The base URL to resolve against. Support String & URL.

htmlnano.process(html, {
minifyUrls: 'https://example.com' // Valid configuration
});
htmlnano.process(html, {
minifyUrls: new URL('https://example.com') // Valid configuration
});
htmlnano.process(html, {
minifyUrls: false // The module will be disabled
});
htmlnano.process(html, {
minifyUrls: true // Invalid configuration, the module will be disabled
});

Example

Basic Usage

Configuration:

htmlnano.process(html, {
minifyUrls: 'https://example.com'
});

Source:

<a href="https://example.com/foo/bar/baz">bar</a>

Minified:

<a href="foo/bar/baz">bar</a>

With sub-directory

Configuration:

htmlnano.process(html, {
minifyUrls: 'https://example.com/foo/baz/'
});

Source:

<a href="https://example.com/foo/bar">bar</a>

Minified:

<a href="../bar">bar</a>

removeOptionalTags

Remove certain tags that can be omitted, see HTML Standard - 13.1.2.4 Optional tags.

Example

Source:

<html><head><title>Title</title></head><body><p>Hi</p></body></html>

Minified:

<title>Title</title><p>Hi</p>

Notice

Due to the limitation of PostHTML, htmlnano can't remove only the start tag or the end tag of an element. Currently, htmlnano only supports removing the following optional tags, as htmlnano can remove their start tag and end tag at the same time:

  • html
  • head
  • body
  • colgroup
  • tbody

normalizeAttributeValues

Normalize casing of attribute values.

The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression.

Example

Source:

<form method="GET"></form>

Minified:

<form method="get"></form>