Reduce main-thread work: where the time goes
The main thread parses, styles, lays out, paints and runs scripts. How to see where its time goes, what Lighthouse flags, and the fix for each kind of work.
In this guide
What the main thread does
MDN describes the path from code to pixels as the critical rendering path. The browser turns the HTML into the Document Object Model (DOM) and the CSS into the CSS Object Model (CSSOM). It combines the two into a render tree that holds the visible content and its styles. Then it works out the size and position of everything, which is layout, and finally paints the pixels.
JavaScript sits in the middle of all this. A script can add or remove elements, change styles and ask for measurements, which sends the browser back to earlier steps. Input events, timers and network callbacks run on the same thread.
Two things follow:
- Everything shares one thread. A task that runs for a long time delays all of the others. See long tasks.
- Style, layout and paint are paid for again after every change. MDN lists the steps of the rendering waterfall: recalculate style, layout, paint, and sometimes composition. To keep animations smooth at 60 frames a second, all of this has to fit in 16.7 milliseconds per frame.
What Lighthouse reports
Lighthouse 13.5 measures main-thread work with several diagnostics. None of them counts towards the performance score, which takes this work into account only through Total Blocking Time. They are leads.
| Audit | What it shows | How it scores |
|---|---|---|
| Minimize main-thread work | The time spent in each group of work | 90 at about 2.0 seconds, 50 at 4.0 seconds |
| Reduce JavaScript execution time | For each script of 50 milliseconds or more: total time, running it, and parsing and compiling it | 90 at about 1.3 seconds, 50 at 3.5 seconds |
| Avoid long main-thread tasks | The 20 longest tasks of 50 milliseconds or more, with the script behind each | A list, no score |
| Optimize DOM size | The biggest layout and style updates, with the size of the DOM | Informational |
| Forced reflow | The functions that asked for a measurement right after changing the page | Fails when any are found |
| Avoid non-composited animations | Animations that the browser could not hand to the graphics processor | Informational |
The groups in the first audit are:
- Script Evaluation: running JavaScript.
- Script Parsing & Compilation: reading and preparing JavaScript for running.
- Style & Layout: working out which styles apply and where everything goes.
- Rendering: preparing and drawing the pixels.
- Parse HTML & CSS: reading the document and the style sheets.
- Garbage Collection: freeing memory the page no longer uses.
- Other: the rest.
The lab test slows the processor down four times to imitate a mid-range phone, so these numbers are bigger than you would see on a fast laptop. That is intended. The numbers are best used to compare one group with another and one test with another.
See your own breakdown
In the report. Open Minimize main-thread work and read the table. Then open Reduce JavaScript execution time to see which scripts are responsible, and notice whether their time is in running the code or in parsing and compiling it.
In Chrome DevTools. Record a page load in the Performance panel. The recording can be filtered by four kinds of activity, Loading, Scripting, Rendering and Painting, by switching the checkboxes off, so you can see how much of the recording each one takes up. Click a long task and read its call tree to see which function is behind it. The Lighthouse guide shows how to save a trace to compare later.
For real visitors. Interaction to Next Paint shows how often the main thread is too busy to answer. See the INP guide.
The fix for each kind of work
| Largest group | Usual cause | What to do |
|---|---|---|
| Script Evaluation | Big bundles, frameworks, tag managers, third-party scripts | Send less code (unused code), delay or remove scripts from other companies (third-party scripts), use defer (script loading), and break up what is left (long tasks), or move heavy work that does not touch the page to a web worker |
| Script Parsing & Compilation | Large files, code that is duplicated or written for old browsers | Smaller bundles, split by page, and drop code that modern browsers do not need (bundle size and code splitting) |
| Style & Layout | A large DOM, complicated selectors, scripts that force layout | Fewer elements, simpler styles, and measurement and changes kept apart |
| Rendering | Large images to decode, heavy effects, animations that cannot use the graphics processor | Resize images (image optimization), animate only transform and opacity |
| Parse HTML & CSS | Very large HTML pages, very large style sheets | Smaller documents, CSS for the page only |
| Garbage Collection | Code that creates many short-lived objects | Allocate less, reuse objects |
Style and layout in a little more detail
The web.dev article on style calculation says that, roughly, half of the time Chrome's engine spends on a computed style goes to matching selectors and half to building the resulting style. The worst-case cost is the number of elements multiplied by the number of rules, because each element must be checked against each rule. The practical advice is to reduce the number of elements that a change invalidates, and to keep selectors simple.
Layout is almost always scoped to the whole document, and larger DOMs generally cost more to lay out. web.dev warns against forced synchronous layout. This happens when a script changes a style and then asks for a measurement such as offsetHeight. To answer, the browser must first apply the change and lay the page out. In a loop it repeats for every item. The fix is to read all the measurements first, then make all the changes. Lighthouse's Forced reflow insight points at the functions that cause it. See DOM size and rendering costs.
Animations
MDN explains that properties that change an element's geometry or position cause style recalculation, layout and repaint. Properties that only change appearance cause recalculation and repaint. A few, such as transform and opacity, can be handled in their own layer and cost only the recalculation. That is why moving things with transform is smoother than changing top or left. Lighthouse's Avoid non-composited animations audit warns that animations which are not composited can be janky and increase layout shift.
A worked example
Suppose a report on a phone shows 4.8 seconds of main-thread work. These numbers are made up to show the method. Script Evaluation takes 2.9 seconds, Style & Layout 0.9, Script Parsing & Compilation 0.5, Rendering 0.3, and the rest 0.2.
The biggest group is Script Evaluation, so the first question is which scripts. In Reduce JavaScript execution time the table shows a tag manager script with 1.1 seconds, the main bundle with 0.9 and a chat widget with 0.6. That points to three decisions:
- Is the chat widget needed on every page? If not, load it only on the contact page, or after a click.
- What does the tag manager load? Remove the tags that nobody looks at.
- How much of the main bundle runs on this page? If most of it belongs to other pages, split it.
After those changes the test is run again, five times, with the median taken. See why scores change. The Style & Layout group at 0.9 seconds comes second. It is not worth touching until the larger one is dealt with.
Mistakes to avoid
- Optimizing the small group first. Fix the largest one, then look again.
- Obsessing over selector speed. MDN notes that selector optimization saves microseconds and is rarely the best place to start. Measure first. The number of elements a change touches matters more.
- Trusting the lab numbers as exact. They come from a simulated phone. Use them to find causes and to compare, and use real-visitor data to judge the result.
- Fixing one page only. Check the home page, a typical content page and the heaviest page, such as a store category.
Check your own page
Run the free audit and open the main-thread work and JavaScript execution time lists. Note the biggest group and the three biggest scripts. The total blocking time guide explains how the same work turns into the lab number that counts towards the score.
Sources
- Critical rendering path and Animation performance and frame rate on MDN.
- Reduce the scope and complexity of style calculations and Avoid large, complex layouts and layout thrashing on web.dev.
- Minimize main-thread work on Chrome for Developers.
- Performance features reference in Chrome DevTools.
Start here
Free website audit
Live Google data for any public page: mobile and desktop.
Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it
Rather have this done for you? Speed optimization · Book a call