
The Browser's Main Thread Is Expensive: Spending It Wisely
A technical post from the kciter.so blog explains why the browser's main thread is the scarcest resource in a web app, and how splitting, batching, prioritizing and deferring keep interfaces responsive.
Most frontend optimization talk revolves around network requests, bundle size and caching. The main thread rarely comes up, because on ordinary screens it never becomes a problem. On screens with heavy interaction, though, nothing you save on the network matters if the thread that runs your code is blocked. In the post "The Browser's Main Thread Is Expensive", the kciter.so blog examines how to spend that resource carefully.
One thread, two jobs
The main thread runs JavaScript — your code, event handlers, timers and framework internals — and it draws the screen: style calculation, layout, paint. A smooth page needs a frame at the display's refresh rate: about 16.6 milliseconds at 60Hz, with a practical budget of roughly 10ms once browser overhead is subtracted. JavaScript follows a single-threaded event loop, so while one task runs, nothing else happens: a 200-millisecond function freezes repaint, animation and input for those 200ms. Tasks beyond 50ms are classed as long tasks, and metrics such as INP and TBT essentially measure how long the main thread was blocked. Jank is usually not slow code — holding the thread is enough.
Splitting, batching, prioritizing, deferring
The post groups the techniques into four moves. Splitting cuts long work into pieces and yields the thread in between, for example after every 20 chat messages or with a 5ms-per-frame budget anchored to requestAnimationFrame. Yielding does not make work faster; it creates the gaps in which the browser can handle input and repaint. Batching trims tasks that fire too often — debounce and throttle for scroll and input events, one draw per frame for live dashboards.
Prioritizing decides order: a queue fed by MessageChannel lets work for a photo the user just clicked jump ahead of background preview generation, the "idle-until-urgent" pattern that React's startTransition embodies. Deferring asks whether work must happen now: code splitting for the initial load, IntersectionObserver to build only the posts near the viewport, and stopping animations that scrolled off screen.
Leaving the main thread
Some work can be handed off entirely. Animating transform and opacity stays on the compositor thread and remains smooth even when JavaScript is busy, while animating top or width forces layout every frame; the FLIP technique turns a real layout change into a transform-based animation. A web worker can take heavy computation such as parsing or image processing, though it cannot touch the DOM and normally exchanges data by copying — unless a buffer is transferred, moving ownership at close to zero cost.
Not doing the work at all
The largest gains often come from eliminating work: dropping stale entries when the inflow outpaces throughput, merging backlogged updates into the latest value, memoizing repeated computations. Real-time apps — streaming platforms, editors, maps, games — feel slow the moment the main thread gets busy, and not every user has the newest hardware.
Before making a task faster, the post argues, ask whether that work has to happen, now, here, at all.
SiTech — AI-powered web development
We build fast, modern websites and bring AI into real business workflows. Have a project or a question? We'd love to help.