All fix guides
WarningPerformance & Core Web Vitals

Reduce JavaScript Execution Time: How to Fix It

Your audit flagged high JavaScript bootup time: the browser spends too long parsing, compiling, and executing scripts before the page becomes responsive. This blocks the main thread, delays interactivity, and drags down Core Web Vitals like INP and Total Blocking Time. The fix is to ship less JavaScript: remove unused code, split bundles so each page loads only what it needs, defer non-critical scripts, and rein in heavy third-party tags. Below is what the issue means, why it matters for both Google rankings and AI answer engines, and how to fix it on common platforms.

What this means

JavaScript execution time is the total time the main thread spends parsing, compiling, and running your scripts. The high_js_bootup code fires when that work crosses a healthy threshold. Lighthouse's "Reduce JavaScript execution time" audit (internally, bootup-time) starts warning once main-thread script work passes roughly 2 seconds on its emulated mid-tier mobile CPU, and fails it past 3.5 seconds.

This is not the same as download time. Even a small, fully cached JS file still costs execution time, because the CPU has to parse and run it. A 300KB bundle of application code can take far longer to execute than a 300KB image takes to decode. On the mid-range phones Google emulates in field data, that cost is amplified because the CPU is much slower than a developer's laptop.

While the main thread is busy running JavaScript, it cannot respond to taps, clicks, or keystrokes, and it cannot paint. That is why heavy bootup shows up as poor Total Blocking Time (TBT) in the lab and poor Interaction to Next Paint (INP) in the field. The flag means the page is doing too much scripting work up front, usually from oversized bundles, hydration of large component trees, or third-party tags (analytics, chat widgets, A/B testing, tag managers).

Why it matters

Core Web Vitals are a confirmed Google ranking signal, and heavy JavaScript execution is one of the most common causes of failing INP, the responsiveness metric that replaced First Input Delay in March 2024. Google rates INP "good" at 200ms or under at the 75th percentile; long main-thread tasks from JS bootup routinely push it past that. Poor field vitals can hold a page back in competitive results.

Execution time is also a direct user-experience issue. When the main thread is locked, the page looks loaded but taps do nothing and forms feel laggy, so users bounce. This hits hardest on mid-range Android devices, where the same bundle can take several times longer to execute than on a desktop.

For AI answer engines the angle is different but real. The crawlers behind ChatGPT and Perplexity generally fetch your raw HTML and do not execute client-side JavaScript. If your content only appears after heavy client-side JS runs, those engines can see an empty or skeleton page and skip you. Google does render JavaScript, but rendering is queued and deferred, so a bloated bundle slows that pipeline too. Shipping your important content in server-rendered HTML, the same discipline that lowers execution time, is also what makes your pages readable to AI crawlers.

How to fix it

  1. 1

    Profile the page to find what actually runs

    Open Chrome DevTools, go to the Performance panel, and record a page load with CPU throttling set to 4x or 6x to approximate a real phone. Look for long tasks (blocks over 50ms) and expand them to see which scripts they belong to. Run Lighthouse and open its "Reduce JavaScript execution time" and "Reduce unused JavaScript" audits, which name the exact files and the milliseconds each one costs. Use the Coverage tab (Ctrl/Cmd+Shift+P, then "Show Coverage") to see what percentage of each bundle is never used. This tells you whether the problem is your own code, a framework, or a third-party tag.

  2. 2

    Ship less code: split bundles and remove dead code

    The most reliable fix is sending fewer bytes of JavaScript. Enable code splitting so each route loads only the JS it needs, and use dynamic import() for components that are not needed on first paint (modals, carousels, editors, charts). Turn on tree-shaking and minification, which are on by default in production builds for Webpack, Vite, esbuild, and Next.js. Audit dependencies: replace heavy libraries like moment.js with date-fns or the native Intl API, and drop utility libraries you use one function from. Every kilobyte you delete is parse and execution time you never pay again.

  3. 3

    Defer and lazy-load non-critical scripts

    Add the defer attribute to script tags so they download in parallel but execute after HTML parsing finishes, in document order, keeping the main thread free during initial render. Use async only for independent scripts like analytics that do not depend on the DOM or on each other. For anything below the fold or triggered by interaction (chat widgets, comment embeds, video players, social buttons), load it on scroll or on click instead of at page load. This alone can move several hundred milliseconds of execution out of the critical path.

  4. 4

    Rein in third-party tags and tag managers

    Third-party scripts are often the biggest offenders and the hardest to spot, because a tag manager can inject many tags that each run their own JavaScript. Audit every tag, remove the ones you no longer use, and load the rest as late as possible. Consider running third-party scripts in a web worker off the main thread (Partytown, or Cloudflare Zaraz), or self-host and defer analytics. Set a performance budget so no single tag can silently add a second of execution time. Marketing tags are the most common reason a page scores well in the lab but fails INP in the field.

  5. 5

    Prefer server-rendered HTML over heavy client hydration

    On a JS framework (React, Vue, Angular), large client-side hydration is a top source of bootup cost, because the browser rebuilds and reattaches your entire component tree in JavaScript. Use server-side rendering or static generation so the HTML arrives ready, and adopt patterns that reduce hydration: React Server Components, islands architecture (Astro), or partial hydration so only interactive widgets ship JS. This cuts execution time and puts your real content in the initial HTML, where AI crawlers and Google's first-pass indexer can read it without running any script.

  6. 6

    Re-audit and set a budget to prevent regressions

    After your changes, re-run the audit and a throttled Lighthouse pass to confirm execution time and TBT dropped, then watch field INP in PageSpeed Insights (CrUX) or Search Console over the following weeks, since field data lags real-user traffic. Lock the win in with a JavaScript size budget in CI, for example failing the build if the main bundle grows past a set kilobyte limit, so a future dependency or tag does not quietly undo the fix.

Example

<!-- Analytics: independent, can run whenever it downloads -->
<script async src="/js/analytics.js"></script>

<!-- App code: runs after HTML is parsed, in order -->
<script defer src="/js/app.js"></script>

<!-- Heavy chat widget: don't load it at page load.
     Inject it only when the user is about to need it. -->
<script defer>
  const loadChat = () => {
    if (window.__chatLoaded) return;
    window.__chatLoaded = true;
    const s = document.createElement('script');
    s.src = 'https://widget.example.com/chat.js';
    s.async = true;
    document.body.appendChild(s);
  };
  // Load on first interaction or when the button scrolls into view
  document.getElementById('open-chat')
    ?.addEventListener('click', loadChat, { once: true });
  window.addEventListener('scroll', loadChat, { once: true, passive: true });
</script>

Defer non-critical scripts and lazy-load a heavy third-party widget only when it's needed, keeping it off the main thread during initial load.

Platform-specific steps

WordPress (Yoast / Rank Math)

Yoast and Rank Math don't control script loading, so use a performance plugin. WP Rocket, Perfmatters, or the free Flying Scripts add a "delay JavaScript until interaction" toggle, which is the single highest-impact option for third-party and theme scripts. Enable it and add your analytics, chat, and embed scripts to the delay list. Use Query Monitor or Asset CleanUp to find and dequeue scripts (sliders, contact-form JS) on pages that don't use them. If a page builder like Elementor only runs on a few pages, avoid loading its JS site-wide.

Shopify

Shopify's platform JS is largely fixed, so focus on apps and theme code. Every installed app can inject scripts that run on every page: uninstall apps you no longer use, since uninstalling (not just disabling) is what removes their code. In your theme, load non-critical custom scripts with defer and lazy-load third-party embeds. Use Lighthouse to identify which app is the biggest execution cost, then contact that vendor or replace the app. Keep tag-manager tags lean.

Next.js / React

Use next/script with the right strategy: afterInteractive for most tags and lazyOnload for chat and analytics you don't need immediately. Use next/dynamic with { ssr: false } for heavy client-only components so they code-split automatically. Prefer Server Components and static generation (App Router) so content ships as HTML with minimal hydration. Note that strategy="worker" (Partytown) is still experimental and only works in the Pages Router, not the App Router; to move third-party tags off the main thread on App Router, use Partytown directly or an edge tag manager. Run @next/bundle-analyzer to find oversized dependencies and split or replace them.

Wix / Squarespace

On these hosted platforms you can't edit the build, so your levers are the tags and embeds you add. Remove unused third-party embeds, custom code blocks, and marketing pixels, which are the main execution cost you control. Consolidate tracking through a single tag manager and add only what you truly need. In Squarespace, prefer native blocks over custom-code embeds where possible. Neither platform lets you defer its core JS, so keeping added scripts minimal is the practical fix.

Cloudflare

Cloudflare can help but won't fix bloated app code. Rocket Loader defers script execution, but test it carefully because it can break scripts that expect to run synchronously. A cleaner option is Cloudflare Zaraz, which loads and manages third-party tags off the main thread, directly cutting execution time from analytics and marketing tags. Auto Minify is deprecated, so minify in your build instead. Edge caching speeds delivery but does not reduce the CPU cost of running the JS once it arrives.

Free tool
Check this with the LCP/CLS Culprit Finder

Frequently asked

What is a good JavaScript execution time?

There is no single official number, but Lighthouse's execution-time audit starts warning past roughly 2 seconds of main-thread script work on its emulated mid-tier mobile CPU and fails past 3.5 seconds. In practice, aim to keep Total Blocking Time under 200ms in the lab, which usually means keeping main-thread JS work well under a couple of seconds. The real target is the field metric it feeds: Interaction to Next Paint at or below 200ms.

What's the difference between reducing JavaScript execution time and reducing unused JavaScript?

They overlap but are not the same. "Reduce unused JavaScript" is about shipping code the page never runs, which you can tree-shake or code-split away. "Reduce JavaScript execution time" (this issue) is about the CPU cost of the code that does run. Removing unused JS usually lowers execution time too, but you can still have high execution time from code that is fully used yet inefficient or simply too much. Fixing both together gives the best result.

Does deferring JavaScript hurt SEO or break my page?

No. defer is safe and recommended. Deferred scripts still execute, just after the HTML is parsed and in document order, so dependencies stay intact. It does not hide content from Google, which renders JavaScript. The one thing to check is any script that must run before first paint (rare); keep those inline or non-deferred and defer the rest. Use async only for independent scripts, since async execution order is not guaranteed.

Do AI search engines like ChatGPT and Perplexity run my JavaScript?

Generally no. The crawlers behind ChatGPT, Perplexity, and most AI answer engines fetch your raw HTML and do not execute client-side JavaScript. If your content only appears after JS runs, those engines can see an empty page and skip you. Server-rendering your content, the same fix that reduces execution time, ensures both AI crawlers and Google's first indexing pass can read it. Google does render JavaScript eventually, but that rendering is queued and slower.

Why does my page score fine on my laptop but fail in the field?

Because Google's field data (CrUX) comes from real users, many on mid-range Android phones with far slower CPUs than a developer machine. JavaScript that runs in half a second on your laptop can take several times longer on those devices. Test with CPU throttling in DevTools (4x to 6x) and trust field INP in Search Console over your local lab numbers.

Does your site have this issue?

Run a free, AI-powered audit and we’ll flag this and 150+ other checks in about a minute. No signup.

No signup needed. Results in under 60 seconds.

Related fixes