Unminified JavaScript files can be 60-80% larger than necessary. Lighthouse flags scripts where over 10% of bytes could be saved through minification—wasted bytes that directly delay your LCP.
Minification removes whitespace, shortens variable names, and eliminates dead code without changing functionality. When you ship unminified JavaScript, you're transferring unnecessary bytes that serve no purpose in production.
Lighthouse triggers this audit when a script has more than 10% potential savings AND at least 2KB could be saved. That threshold exists because minification always provides some benefit—only scripts with significant waste get flagged.
Why it hurts LCP: JavaScript must download and parse before your page becomes interactive. Unminified scripts take longer to download (more bytes) and longer to parse (more tokens for the engine to process). If your LCP element depends on JavaScript to render—common in SPAs—those extra milliseconds directly delay your largest paint.
Consider a typical React app bundle. An unminified version might be 800KB. Minified with Terser, it drops to 250KB. With gzip compression on top, it becomes 80KB. That's a 10x reduction in transfer size from two simple build steps.
Look for the audit "Minify JavaScript" under Opportunities. It shows:
Network Tab Method:
Sources Tab Method:
.min.js suggests minification, plain .js may not beMost bundlers minify automatically in production mode.
Vite (recommended):
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
build: {
minify: 'terser', // or 'esbuild' (default, faster)
terserOptions: {
compress: {
drop_console: true, // Remove console.log
drop_debugger: true // Remove debugger statements
}
}
}
})
Vite uses esbuild by default (faster) but Terser produces smaller output.
Webpack:
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'production', // Enables minification
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
},
mangle: true,
format: {
comments: false,
},
},
extractComments: false,
}),
],
},
}
Rollup:
// rollup.config.js
import terser from '@rollup/plugin-terser'
export default {
plugins: [
terser({
compress: {
passes: 2,
drop_console: true,
},
mangle: true,
})
]
}
After configuring, verify your output is actually minified.
ls -la dist/assets/*.js
Inspect the output file—it should be dense, single-line code:
// Unminified (don't ship this)
function calculateTotal(items) {
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
// Minified (ship this)
function calculateTotal(e){let t=0;for(const l of e)t+=l.price*l.quantity;return t}
Self-hosted libraries might not be minified. Check and replace.
<!-- Before: unminified -->
<script src="/js/lodash.js"></script>
<!-- After: minified -->
<script src="/js/lodash.min.js"></script>
For npm packages, ensure you're importing production builds:
// Some packages require explicit production imports
import Vue from 'vue/dist/vue.runtime.min.js'
CDNs like Cloudflare can minify JavaScript on the fly.
Cloudflare settings:
This catches any scripts that slip through your build process.
Don't forget <script> tags embedded in HTML.
<!-- Before -->
<script>
// Initialize analytics
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-XXXXXX');
</script>
<!-- After -->
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date);gtag("config","G-XXXXXX");</script>
Use HTML minification plugins to handle this automatically:
// vite.config.js
import { createHtmlPlugin } from 'vite-plugin-html'
export default {
plugins: [
createHtmlPlugin({
minify: true
})
]
}
Minification reduces JavaScript size through three mechanisms:
calculateTotalPrice becomes a, userPreferences becomes bThe result: 60-80% smaller files that parse faster. Smaller files also compress better—Brotli works more efficiently on dense, repetitive minified code.
npm run build
# + /_app 87 kB 87 kB
// next.config.js
module.exports = {
swcMinify: true,
}
// nuxt.config.ts
export default defineNuxtConfig({
vite: {
build: {
minify: 'terser',
}
}
})
npx nuxi build
npm run build -- --mode production
vue.config.js:module.exports = {
productionSourceMap: false, // Smaller builds
configureWebpack: {
optimization: {
minimize: true
}
}
}
After implementing:
File size targets:
| Original Size | After Minification | After Gzip | After Brotli |
|---|---|---|---|
| 100KB | 30-40KB | 10-15KB | 8-12KB |
| 500KB | 150-200KB | 50-70KB | 40-55KB |
| 1MB | 300-400KB | 100-140KB | 80-110KB |
process.env.NODE_ENV === 'production'unsafe) can break functionality. Test thoroughly.Often appears alongside:
Different pages may load different JavaScript bundles—some minified, some not. Unlighthouse scans every page on your site and flags any scripts that aren't properly minified.