Legacy JavaScript in Lighthouse: what to fix, what to leave
Code for old browsers that every visitor downloads. Whose file Lighthouse points at, and what to do on WordPress, Shopify, Next.js and your own build.
Should you fix Legacy JavaScript?
Lighthouse shows Legacy JavaScript ("Polyfills and transforms enable older browsers to use new JavaScript features") where it used to show Avoid serving legacy JavaScript to modern browsers. It lists scripts that carry code for browsers almost nobody uses, and every visitor downloads it. Fix your own JavaScript by building it for Baseline Widely available browsers; update, replace or leave a plugin's or vendor's file.
Just want the quick fix? Jump to WordPress, Shopify, Next.js or your own build.
What does Legacy JavaScript find in your scripts?
The notice reads: "Polyfills and transforms enable older browsers to use new JavaScript features. However, many aren’t necessary for modern browsers. Consider modifying your JavaScript build process to not transpile Baseline features, unless you know you must support older browsers."

The booking widget of a ferry test page, run with the settings PageSpeed Insights uses: one script with 22 KiB.
Lighthouse groups the scripts by domain ("1st Party" for your own, the company's name for a vendor) and gives each one its "Wasted bytes". Under each script, a line named after a feature, such as Array.prototype.find or Object.entries, is a polyfill: code that adds that feature to browsers that lack it. A line that starts with @babel/plugin-transform is a transform: Babel, the tool that built the file, rewrote modern code, such as a class or an async function, into older JavaScript with helper functions.
The kilobytes are an estimate and can be larger than the file itself. One small fallback for a single feature is counted as if the script carried the shared base code of core-js, the usual polyfill library, so a file that downloads 4 KiB can show 10 KiB.
Red means a listed script downloads at the same time as your largest element (LCP) or your first content (FCP); orange means it only adds kilobytes. Unscored: Legacy JavaScript itself doesn't count toward the Performance score, but LCP and FCP do.
What does legacy JavaScript do to your visitors?
Your visitor waits a little longer for whatever the script builds: a booking form, a menu, a slider. The browser downloads the extra code and runs it, even when it has every feature built in: each polyfill checks for its feature and keeps the browser's own version, but the bytes and the check come on every page load.
On a test page, the home page of a car ferry company, I built the booking widget in React twice from the same code: once for Internet Explorer 11 with Babel and core-js, once for current browsers. Both ran on the same phone connection, a 150 ms round trip at 1.6 Mbps, with the CPU of a mid-range phone:
| Build | Booking form on screen | JavaScript (gzip) | Script running |
|---|---|---|---|
| For Internet Explorer 11 | 1.93 s | 83 kB | 80 ms |
| For current browsers | 1.73 s | 49 kB | 50 ms |

Before

After
The ferry page 1.8 seconds after the visitor tapped the link.
That is 0.2 seconds for one script, 30 ms of it running. A deferred script still shares the connection with the photo at the top of the page, which is why the notice turned red here. A bundle with all of core-js, or a page with several bundles like this one, has more extra code than this widget.
Whose file is Lighthouse pointing at?
The file's address in the report tells you who built it, and that decides what you can do:
- Your own bundle: a theme or app your developer builds. You change one build setting and rebuild (Your own build).
- A plugin's script: on WordPress, the folder after
/wp-content/plugins/in the address is the plugin. Lighthouse lists MonsterInsights'frontend-gtag.min.js, the Stripe gateway for WooCommerce on product and checkout pages, and date pickers in form plugins (flatpickr in WPForms, Ninja Forms and Elementor).wp-polyfill.min.jsis a WordPress file that loads when a plugin asks for it: WooCommerce's blocks or Contact Form 7 with reCAPTCHA switched on (WordPress). - A vendor's tag, listed under the company's name: Meta Pixel (
fbevents.js), the OneTrust cookie banner, HubSpot's form and feedback scripts, and the beacon of Cloudflare Web Analytics, which Cloudflare adds to your pages itself. Google Tag Manager and gtag.js are not listed. Only the vendor can rebuild these files: keep the tag or remove it. You switch off Cloudflare Web Analytics in your Cloudflare dashboard. - Next.js: every Next.js site has a script listed with Next.js's own polyfills, lines such as
Array.prototype.atandObject.hasOwn.

The ferry page with the build for current browsers and a Meta Pixel added: only Facebook's file is left.
How do you fix Legacy JavaScript?
Start with the largest file you control, then the plugin or app that loads old code on every page.
WordPress
Update WordPress and the plugin first: a new version may be built for current browsers. Then, for each listed file:
- A plugin you don't need on every page: a form plugin's date picker or a payment gateway's script belongs on the pages that use it. Unload it everywhere else with Asset CleanUp (menu "Asset CleanUp", "CSS & JS Manager") or the Script Manager in Perfmatters.
- A plugin with an alternative: if an analytics plugin such as MonsterInsights loads its own listed script on every page, switch to one that only adds Google's own tag, such as Site Kit by Google.
wp-polyfill: the Query Monitor plugin lists the scripts that depend on it. Find the plugin behind them and load that plugin only where it is used, such as Contact Form 7's reCAPTCHA on the contact page. Don't unloadwp-polyfillitself in those tools or deregister it in code: WordPress then silently drops every script that needs it, and on a WooCommerce store that is the mini-cart, the cart and the checkout.
"Delay JavaScript execution" in WP Rocket clears the notice, but the visitor still downloads and runs the same code, on the first tap or scroll.
Shopify
A listed file comes from an app, a tag pasted into the theme or a custom theme's own scripts. Update the app or ask its vendor for a current build, remove the apps you no longer use, and send the developer of a custom theme to the build setting.
Next.js
Next.js builds your own code for current browsers by default, and for the browserslist in package.json if you set one. No setting removes the polyfills Next.js adds itself. Leave that line.
Your own build
Tell your build which browsers to support and stop adding polyfills. Baseline Widely available is the set of features every major browser has supported for at least 30 months. Worldwide about 0.2% of visitors use a browser that can't run classes or async functions; check your own analytics for old iPhones stuck on iOS 15 or 16 before you switch. In package.json:
"browserslist": [
"baseline widely available"
]
This needs browserslist 4.26 or newer. With Babel 7, remove useBuiltIns, corejs and targets from @babel/preset-env, and the line import "core-js/stable" if your entry file has it:
// babel.config.js
module.exports = {
presets: ['@babel/preset-env']
};
Without targets, Babel reads the browserslist in package.json: it then only rewrites syntax those browsers lack, and adds no polyfills. Babel 8 dropped useBuiltIns and corejs, and Vite, esbuild and webpack add no polyfills themselves: in a webpack build they come from babel-loader, so the Babel change covers it. Vite also builds for Baseline Widely available by default.
A line that stays after the rebuild comes from a package in node_modules with its own old build: update or replace that package.
Then run Lighthouse again, in the same way as before: it reads "No legacy JavaScript found", or lists only files you don't build.
A script your page doesn't use at all is a different notice: Reduce unused JavaScript.
When can you leave Legacy JavaScript in the report?
Leave it when the listed lines are Next.js's own polyfills, or when the file is a vendor's tag you need and can't replace, as long as the notice is orange. Fix it when the file is your own bundle with tens of KiB, or when the notice is red: then the file downloads together with your LCP element, and a smaller file lets that element arrive sooner. A vendor's tag you can't make smaller can start after your LCP element has loaded instead: ways to load a script later.
A site with ten vendor tags and twenty plugins has a different question: which of those scripts each page needs at all. In a power hour we open the report of your own pages and settle which listed scripts stay, and on which pages.

Need a faster site?
I'm Arjen, and I've been fixing slow sites for 17 years. If you're running into issues you can't fix yourself, send me your URL and let's have a chat.
Send me your URL
Why isn't our fix working?
Sometimes the CDN still serves the old HTML, or the fix went into the desktop template and mobile uses another one. Send me your URL and the problem, and I reply with a few times. I check your field data first, then we spend one hour on your site, and within a day you have my notes in writing. €350 excl. VAT.
Send me your URLNo developer to build the fix? I do the work myself, on a copy of your site.