The Browser's Main Thread Is Expensive

The Browser's Main Thread Is Expensive

Frontend optimization usually focuses on network requests, bundle size, and caching. But on interactive screens with live data, the main thread becomes the bottleneck. This article explains how the main thread handles JavaScript and rendering, and why long tasks freeze the UI. It offers four strategies—splitting, batching, prioritizing, and deferring—with practical examples like yielding with setTimeout or requestAnimationFrame to keep the interface responsive.

The code isn’t slow. It just happens to be the code that’s holding the main thread.
  1. kccqzy

    Here is a problem that I had thought about in my last frontend project: the project needed to parse a user-provided string to provide syntax highlighting. The implementation was to parse the string immediately after a user keystroke so the user always sees correct colors. But I had this nagging thought that if I had designed the grammar wrong some parses might take more than linear time and would hold the main thread during the parse. But I didn’t really want to first render a black string, parse on a service worker and then rerender with color. The effect must have seemed disorienting.

    Anyways I stopped the project for unrelated reasons and this thought kept nagging me in the back of my mind.

  2. martinald

    Good article and I wish this was much better known.

    The issue is though that it's too focused on interactivity. In reality, 90%++ of slow sites are not slow because of interactivity really, they are slow because they ship enormous react/nextjs bundles and have extremely heavy hydration work to do.

    _so many_ sites have bundles >10MB that need to be downloaded, parsed and hydrated.

    I've even seen (many) sites which have multiple SPAs stacked inside of them.

    If you're on a slow internet connection and/or CPU the page is basically unusable for many tens of seconds and no amount of yielding post bundle hydrate will really solve that.

  3. nerdralph

    I agree with most of the article, but would clarify the following:

    > For the screen to look smooth, frames have to be drawn at the display’s refresh rate. On the most common 60Hz display, that means 60 frames per second, or about 16.6 milliseconds per frame.

    It isn't absolutely necessary to match the display refresh rate. With a 144Hz monitor, 72FPS is going to look smooth for the vast majority of the people, and even 48FPS will look smooth for most people. I agree that higher FPS is better, but there are diminishing returns.

  4. jonathanlydall

    Superb article.

    There was very little new information for me as I have applied some of these techniques myself based on having developed an intuitive understanding on how a rendering "thread" works and blocks, but for a less experienced developer this article should be incredibly enlightening and provide a solid understanding of what's happening.

    I employed use of yielding on a hobby project [0] I made about 15 years ago, particularly when it had to do lots of draw operations on a canvas. I also experimented with using worker threads to render pieces of it on a background thread, but at the time there was no way to copy the data efficiently between them and the main thread, one had to send the data as a base64 encoded PNG and the overhead of encoding, decoding and then copying it onto the canvas made it perform far worse than just doing it all on the main thread.

    The website also does Gzip decoding of uploaded files in JavaScript and the library I found at the time did all the work synchronously so could lock up the UI thread easily for 10+ seconds. I tweaked it to be able to yield every 200ms or something, the process of which was very educational, particularly due to it convincing me to never omit the curly braces after an if statement, I spent a very long time trying to understand why it wasn't working until eventually I realized a statement I added wasn't in the if statement's block. It's not that I didn't understand how if statements worked, it's that in my mind the lack […]

  5. larodi

    Top article, kudos! applied some of these techniques previously, and they do matter a lot. thing is, when you learn/teach JS there is usually limited time left to talk these topics, and they are essential more than it seems.

    the whole point of cooperative multitasking is to yield now and then (back to the runner), so that it can process the drawing. Besides, at 60fps there's so much that can be computed once every N frames, and the eye does not see it, the animation flows.

    It becomes even more interesting when webgpu is involved, but the CSS animator is indeed very fast.

    note: some recent work of mine (newskool digital flyer sites) -> bsf.hmsu.org // nouveauxhivers.dub4powder.xyz

  6. gwbas1c

    FYI: This isn't a browser-only thing. All platforms (Windows, Mac, ios, android) typically have a single-threaded UI.

  7. piker

    Great piece, OP. We work in WASM and are considering web workers to help with rendering documents off the main thread in that context. I was surprised to read that ArrayBuffer is moved by reference only! Might be an option. Thanks for putting this together.

  8. jkhdigital

    This article concludes with the following statement:

    > So much of development is trade-offs, and you have to choose according to the situation, which ultimately comes down to the developer’s experience and judgment.

    It’s a great article, but I think it is a bit behind the ball in framing scheduling problems as a matter of “experience and judgment”. The problem of allocating work to a scarce resource is one of the oldest and most well-studied in all of computer science. It would make sense to go consult a textbook on the matter before trying to reinvent the wheel.

More from this day

2026-09-03