Legacy JavaScript medium
Avoid serving legacy JavaScript to modern browsers
Your build ships Babel transforms and core-js polyfills to browsers that already support modern JavaScript, padding every bundle.
What you see
Avoid serving legacy JavaScript to modern browsers — Est savings of 47 KiB Polyfills and transforms enable older browsers to use new JavaScript features. However, many aren't necessary for modern browsers.
What’s actually happening
Bundles are bigger than they should be and the audit lists specific files with the dead weight inside them. Expand a row and Lighthouse points at the exact culprit — a core-js module, a @babel/plugin-transform, or a regenerator-runtime shim. The waste is pure download and parse cost for features Chrome, Firefox, and Safari have shipped for years.
Common causes
- A browserslist config (or default) that still targets dead browsers like IE 11, forcing Babel to transpile and inject polyfills nobody needs.
- @babel/preset-env with useBuiltIns set wrong, or no targets, so it polyfills everything.
- Importing core-js or @babel/polyfill wholesale at the top of your entry file instead of letting preset-env inject only what's used.
- Shipping a single bundle to every browser instead of differentiated modern/legacy builds.
- A regenerator-runtime pulled in to support transpiled async/await that every current engine runs natively.
How to fix it
- Fix your browserslist targetSet a realistic target in package.json or .browserslistrc — e.g. "defaults" or "> 0.5%, last 2 versions, not dead". Drop IE entirely. Run npx browserslist to see exactly which browsers you're building for, then confirm the polyfill list shrinks.
- Configure preset-env correctlyIn Babel use { useBuiltIns: "usage", corejs: 3 } so it injects polyfills per-file only for syntax you actually use against your target list — not a blanket import.
- Ship module/nomoduleEmit a modern ES2017+ build served via <script type="module"> and a transpiled fallback via <script nomodule>. Modern browsers download only the module build and ignore nomodule; old browsers do the reverse. Vite does this automatically with @vitejs/plugin-legacy.
- Remove blanket polyfill importsDelete any top-level import "core-js"; or import "@babel/polyfill"; lines. Let preset-env's usage mode handle it, or import single polyfills only where genuinely required.
Stop it recurring
Pin browserslist in source control and review it each year so the target tracks reality instead of inheriting a decade-old default.
Related errors