Fix Unminified JavaScript for Better LCP
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.
What's the Problem?
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.
How to Identify This Issue
Lighthouse
Look for the audit "Minify JavaScript" under Opportunities. It shows:
- Each unminified script URL
- Total transfer size
- Potential savings in KB
- Red flag if savings exceed 8KB per file
Chrome DevTools
Network Tab Method:
- Open DevTools → Network tab
- Filter by "JS" type
- Click a script file
- Look at the Response tab—readable code with whitespace indicates unminified
Sources Tab Method:
- Open DevTools → Sources tab
- Navigate to your JS files
- If code is readable with full variable names and comments, it's unminified
- Minified code appears as long, dense lines with single-letter variables
Coverage Tab
- Open DevTools → More tools → Coverage
- Click the reload button
- Red bars indicate unused code, but check file names—
.min.jssuggests minification, plain.jsmay not be
The Fix
1. Configure Your Bundler for Production Mode
Most 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,
})
]
}
2. Verify Build Output
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}
3. Minify Third-Party Scripts
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'
4. Use a CDN with Automatic Minification
CDNs like Cloudflare can minify JavaScript on the fly.
Cloudflare settings:
- Speed → Optimization → Auto Minify
- Enable "JavaScript" checkbox
This catches any scripts that slip through your build process.
5. Minify Inline Scripts
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
})
]
}
Why This Works
Minification reduces JavaScript size through three mechanisms:
- Whitespace removal — Spaces, tabs, and newlines add zero value in production
- Identifier mangling —
calculateTotalPricebecomesa,userPreferencesbecomesb - Dead code elimination — Unused exports and unreachable branches are stripped
The result: 60-80% smaller files that parse faster. Smaller files also compress better—Brotli works more efficiently on dense, repetitive minified code.
Framework-Specific Solutions
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
}
}
}
Verify the Fix
After implementing:
- Run Lighthouse → "Minify JavaScript" should pass or show minimal savings
- Check DevTools Sources → JS files should be dense, unreadable code
- Compare file sizes before/after (aim for 60-80% reduction)
- Test in staging to ensure functionality isn't broken
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 |
Common Mistakes
- Forgetting to set NODE_ENV=production — Many tools only minify when
process.env.NODE_ENV === 'production' - Shipping source maps to production — Source maps help debugging but add significant bytes. Disable or host separately.
- Double-minifying — Minifying already-minified code wastes build time and can break code
- Aggressive compression breaking code — Some Terser options (like
unsafe) can break functionality. Test thoroughly. - Ignoring vendor bundles — Split vendor code so it can be cached separately, but ensure it's still minified
Related Issues
Often appears alongside:
- Unused JavaScript — Even minified unused code wastes bytes
- Total Byte Weight — Unminified JS inflates total page weight
- Render-Blocking Resources — Large JS files block rendering longer
Test Your Entire Site
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.