Minification removes everything browsers don’t need from CSS and JavaScript (whitespace, comments, long variable names), typically shrinking files by 20–60% before compression. Combined with gzip or Brotli compression on the server, and with good caching, it’s a basic step for fast pages.

Minification vs compression

Technique Where Effect
Minification Build step Smaller source code (removes whitespace, shortens names, dead code)
Compression (gzip/Brotli) Web server/CDN, per response Compresses bytes on the wire

Do both: minified files also compress better. See Nginx gzip and Brotli.

Modern build tools do it automatically

If you use a bundler or framework, production builds are already minified:

npm run build        # Vite, Next.js, Nuxt, Astro, SvelteKit, Angular: minified output
  • Vite uses esbuild (JS) and Lightning CSS or esbuild (CSS).
  • webpack uses Terser and CSS minimizers in production mode.
  • Check that you deploy the build output (dist/), not the source files.

Minify standalone files

For simple sites without a bundler:

npm install -g esbuild lightningcss-cli
esbuild src/app.js --minify --outfile=dist/app.min.js
esbuild src/style.css --minify --outfile=dist/style.min.css
# or: lightningcss --minify --targets '>= 0.25%' src/style.css -o dist/style.min.css

Terser (JavaScript) and cssnano (CSS, via PostCSS) are other popular choices.

Remove unused CSS

CSS frameworks ship thousands of rules your pages never use. Remove them:

  • Tailwind CSS scans your templates, and only generates the classes you use.
  • PurgeCSS removes unused selectors from other frameworks (Bootstrap and others). Safelist classes added dynamically by JavaScript.

This often saves far more than minification.

Bundling and code splitting

  • HTTP/2 and HTTP/3 make many small files cheap, so you no longer need to concatenate everything into one huge bundle. See HTTP/2 and HTTP/3 in Nginx.
  • Code splitting: load only the JavaScript a page needs (dynamic import(), per-route chunks). Frameworks do this automatically.
  • Tree shaking: bundlers drop unused exports. Import only what you use (import { debounce } from "lodash-es", not the whole library).
  • Analyse bundles (vite-bundle-visualizer, webpack-bundle-analyzer) to find heavy dependencies.

Cache busting

Minified build output usually has hashed names (app.4f2a1c.js), so it can be cached for a year. See HTTP caching headers.

Source maps

Minified code is unreadable when debugging production errors. Generate source maps, and either:

  • upload them to your error tracker (Sentry, GlitchTip) without serving them publicly; or
  • serve them, accepting that your source becomes readable.

See error tracking with Sentry.

WordPress and CMS sites

Optimization plugins (Autoptimize, WP Rocket, LiteSpeed Cache) minify and combine theme and plugin assets. Enable options one at a time, and test: aggressive combining and “delay JS” settings break sites more often than they speed them up. See WP Rocket alternatives.

Pitfalls

  • Minifying on every request at runtime (some old PHP plugins do this). Minify at build time, or cache the result.
  • Breaking scripts that rely on function names or comments (rare, but possible with aggressive settings).
  • Minifying already-minified vendor files (wasted build time).
  • Ignoring the bigger wins: unused JS, large images, render-blocking third-party scripts. See website speed optimization.

Measure

Use Lighthouse (“Minify CSS”, “Minify JavaScript”, “Reduce unused JavaScript”), and compare transfer sizes in DevTools before and after. See web performance testing.

Quick reference

Task Tool
Framework projects npm run build (already minified)
Standalone JS/CSS esbuild, Terser, Lightning CSS, cssnano
Unused CSS Tailwind JIT, PurgeCSS
Smaller JS Code splitting, tree shaking, lighter dependencies
On the wire gzip/Brotli in Nginx

Related: WebP and AVIF images and Core Web Vitals and server performance.

  • Performance
  • Web Development
  • Frontend