purify-css and uncss are both utilities designed to reduce final CSS bundle sizes by analyzing source code (HTML, JavaScript, templates) and removing unused selectors from stylesheet files. purify-css operates by scanning content files for class names and IDs, then filtering the CSS based on these matches. It is known for its flexibility with custom content extractors and broad template engine support. uncss, historically a pioneer in this space, uses a headless browser (PhantomJS) to render pages and determine which styles are actually applied in the DOM. This allows it to handle dynamic class additions and complex selectors more accurately than static analysis, but introduces heavier runtime dependencies and configuration complexity.
Both purify-css and uncss solve the same critical problem: removing unused CSS to shrink bundle sizes and improve load times. However, they take fundamentally different approaches to detecting what is "unused," leading to distinct trade-offs in accuracy, performance, and maintenance. Understanding these differences is vital when dealing with legacy systems or deciding whether to migrate to modern alternatives.
The core difference lies in how each tool decides which CSS rules to keep.
purify-css uses static analysis. It reads your source files (HTML, JS, templates) as text, extracts strings that look like class names or IDs using regular expressions, and compares them against your CSS selectors.
// purify-css: Static analysis configuration
const purify = require('purify-css');
purify(['src/**/*.html', 'src/**/*.js'], ['src/**/*.css'], {
output: 'dist/clean.css',
// It simply looks for strings matching class patterns
minify: true
});
uncss uses dynamic rendering. It spins up a headless browser (historically PhantomJS) to actually load your pages, execute JavaScript, and render the DOM. It then snapshots the computed styles to see what is truly being used.
// uncss: Dynamic rendering configuration
const uncss = require('uncss');
uncss(['http://localhost:8080/index.html'], {
css: ['src/styles.css'],
// It loads the URL in a browser to find used styles
ignore: ['.js-added-class']
}, function(error, output) {
console.log(output);
});
Because of their detection strategies, the runtime requirements differ significantly.
purify-css is extremely fast and lightweight. It runs purely in Node.js with no external binaries. You can run it on any CI/CD server without extra setup.
# purify-css: Runs instantly in any Node environment
npm install purify-css
# No additional system dependencies required
uncss is heavy and slow. It requires a headless browser engine. Historically, it depended on PhantomJS, which is now obsolete. Running it often requires installing system-level libraries for browser rendering, making CI/CD setup cumbersome.
# uncss: Requires heavy system dependencies
npm install uncss
# Historically required PhantomJS installation on the OS
# This often fails on modern minimal Docker images or CI runners
Static analysis often struggles with dynamic classes, while rendering engines handle them naturally.
purify-css can miss classes added by JavaScript at runtime if they aren't present as strings in the source code. You often need to manually whitelist these patterns.
// purify-css: May miss dynamic classes unless whitelisted
purify(files, css, {
output: 'clean.css',
// Must manually tell it to keep classes matching this pattern
whitelist: ['.is-active', '/^js-/' ]
});
uncss naturally catches dynamic classes because it executes the JavaScript on the page. If a script adds a class after load, uncss sees it in the DOM and keeps the CSS.
// uncss: Automatically detects runtime classes
// No whitelist needed for classes added by JS execution
uncss(urls, options, function(err, output) {
// The output includes styles for classes added via DOM manipulation
});
This is the most critical factor for architectural decisions today.
purify-css is no longer actively maintained. The original repository has seen little activity in recent years. While it still works for many setups, it lacks support for modern CSS features and newer framework patterns. Developers are strongly encouraged to migrate to purgecss, which is the spiritual successor with active maintenance and a plugin ecosystem.
// purify-css: Deprecated/Inactive
// Recommendation: Migrate to 'purgecss'
// const purgecss = require('purgecss'); // Active alternative
uncss is officially deprecated. The project is archived, and its dependency on PhantomJS (which is also dead) makes it incompatible with modern security standards and build environments. It should not be used in any new project.
// uncss: Deprecated and Archived
// DO NOT USE in new projects.
// The underlying PhantomJS engine is insecure and obsolete.
// Modern alternative: Use PurgeCSS with a custom extractor or critical CSS tools.
Both tools offered plugins for popular loaders, but support varies.
purify-css had simple webpack plugins but required manual configuration for many frameworks.
// purify-css: Webpack plugin example (Legacy)
const PurifyCSSPlugin = require('purify-css-webpack');
module.exports = {
plugins: [
new PurifyCSSPlugin({
paths: glob.sync(path.join(__dirname, 'app/*.js'))
})
]
};
uncss integrated deeply with task runners like Gulp, often as part of a larger pipeline involving server spinning.
// uncss: Gulp task example (Legacy)
const gulp = require('gulp');
const uncss = require('gulp-uncss');
gulp.task('css', function () {
return gulp.src('src/**/*.css')
.pipe(uncss({
html: ['http://localhost:3000']
}))
.pipe(gulp.dest('dist'));
});
| Feature | purify-css | uncss |
|---|---|---|
| Method | Static Text Analysis | Headless Browser Rendering |
| Speed | Very Fast | Slow (Browser startup time) |
| Dynamic Classes | Misses them (needs whitelist) | Detects them automatically |
| Dependencies | None (Pure Node) | Heavy (PhantomJS/Browser) |
| Status | Inactive / Unmaintained | Deprecated / Archived |
| Best For | Legacy static sites (temporary) | None (Obsolete) |
purify-css was a reliable workhorse for simple, static projects where build speed mattered more than 100% accuracy. However, its lack of maintenance makes it a risk for modern stacks. If you are currently using it, plan a migration to purgecss, which offers the same static analysis benefits with active support and better framework integration.
uncss was a powerful tool for complex applications where JavaScript heavily manipulated the DOM. Its ability to "see" the rendered page was unique. However, the death of PhantomJS and the project itself means it is now a liability. Using it today introduces security vulnerabilities and build instability.
Final Recommendation: Do not start new projects with either of these packages. The industry has standardized around purgecss. It combines the speed of static analysis with the ability to write custom extractors for dynamic frameworks (like React, Vue, and Angular), effectively replacing the need for heavy headless browsers while maintaining high accuracy. Use purify-css or uncss only when maintaining older legacy builds that cannot yet be refactored.
Choose purify-css if you need a lightweight, static analysis tool that integrates easily into build pipelines without requiring a browser environment. It is ideal for projects using standard template engines (Handlebars, Pug) or frameworks where class names are explicitly written in the source files. However, be aware that this package is no longer actively maintained, so you should evaluate modern forks like purgecss for long-term projects requiring security updates and framework-specific plugins.
Choose uncss only if you are maintaining a legacy build pipeline that specifically relies on its headless browser rendering to catch dynamically applied styles that static analyzers miss. For all new projects, do not choose this package; it is officially deprecated and unmaintained, relying on obsolete technologies like PhantomJS. Modern alternatives offer better performance, active support, and safer execution environments without the need for a full browser instance.
A function that takes content (HTML/JS/PHP/etc) and CSS, and returns only the used CSS.
PurifyCSS does not modify the original CSS files. You can write to a new file, like minification.
If your application is using a CSS framework, this is especially useful as many selectors are often unused.
Installation
npm i -D purify-css
import purifycss from "purify-css"
const purifycss = require("purify-css")
let content = ""
let css = ""
let options = {
output: "filepath/output.css"
}
purify(content, css, options)
$ npm install -g purify-css
$ purifycss -h
purifycss <css> <content> [option]
Options:
-m, --min Minify CSS [boolean] [default: false]
-o, --out Filepath to write purified css to [string]
-i, --info Logs info on how much css was removed
[boolean] [default: false]
-r, --rejected Logs the CSS rules that were removed
[boolean] [default: false]
-w, --whitelist List of classes that should not be removed
[array] [default: []]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
Statically analyzes your code to pick up which selectors are used.
But will it catch all of the cases?
button-active <!-- html -->
<!-- class directly on element -->
<div class="button-active">click</div>
// javascript
// Anytime your class name is together in your files, it will find it.
$(button).addClass('button-active');
button-active // Can detect if class is split.
var half = 'button-';
$(button).addClass(half + 'active');
// Can detect if class is joined.
var dynamicClass = ['button', 'active'].join('-');
$(button).addClass(dynamicClass);
// Can detect various more ways, including all Javascript frameworks.
// A React example.
var classes = classNames({
'button-active': this.state.buttonActive
});
return (
<button className={classes}>Submit</button>;
);
var content = '<button class="button-active"> Login </button>';
var css = '.button-active { color: green; } .unused-class { display: block; }';
console.log(purify(content, css));
logs out:
.button-active { color: green; }
var content = ['**/src/js/*.js', '**/src/html/*.html'];
var css = ['**/src/css/*.css'];
var options = {
// Will write purified CSS to this file.
output: './dist/purified.css'
};
purify(content, css, options);
var content = ['**/src/js/*.js', '**/src/html/*.html'];
var css = '.button-active { color: green; } .unused-class { display: block; }';
var options = {
output: './dist/purified.css',
// Will minify CSS code in addition to purify.
minify: true,
// Logs out removed selectors.
rejected: true
};
purify(content, css, options);
logs out:
.unused-class
var content = ['**/src/js/*.js', '**/src/html/*.html'];
var css = ['**/src/css/*.css'];
purify(content, css, function (purifiedResult) {
console.log(purifiedResult);
});
var content = ['**/src/js/*.js', '**/src/html/*.html'];
var css = ['**/src/css/*.css'];
var options = {
minify: true
};
purify(content, css, options, function (purifiedAndMinifiedResult) {
console.log(purifiedAndMinifiedResult);
});
// Four possible arguments.
purify(content, css, options, callback);
content argumentArray or StringArray of glob file patterns to the files to search through for used classes (HTML, JS, PHP, ERB, Templates, anything that uses CSS selectors).
String of content to look at for used classes.
css argumentArray or StringArray of glob file patterns to the CSS files you want to filter.
String of CSS to purify.
options argumentObjectminify: Set to true to minify. Default: false.
output: Filepath to write purified CSS to. Returns raw string if false. Default: false.
info: Logs info on how much CSS was removed if true. Default: false.
rejected: Logs the CSS rules that were removed if true. Default: false.
whitelist Array of selectors to always leave in. Ex. ['button-active', '*modal*'] this will leave any selector that includes modal in it and selectors that match button-active. (wrapping the string with *'s, leaves all selectors that include it)
callback argumentFunctionA function that will receive the purified CSS as it's argument.
purify(content, css, options, function(purifiedCSS){
console.log(purifiedCSS, ' is the result of purify');
});
purify(content, css, function(purifiedCSS){
console.log('callback without options and received', purifiedCSS);
});
$ purifycss src/css/main.css src/css/bootstrap.css src/js/main.js --min --info --out src/dist/index.css
This will concat both main.css and bootstrap.css and purify it by looking at what CSS selectors were used inside of main.js. It will then write the result to dist/index.css
The --min flag minifies the result.
The --info flag will print this to stdout:
________________________________________________
|
| PurifyCSS has reduced the file size by ~ 33.8%
|
________________________________________________
The CLI currently does not support file patterns.