Skip to main content

Command Palette

Search for a command to run...

Things Your Browser Does Behind Your Back

Unknown quirks with the browser.

Updated
4 min read
Things Your Browser Does Behind Your Back
T

Just a guy who loves to write code and watch anime.

The Six Connection Limit

Browsers limit how many requests they send to the same domain at the same time. For HTTP/1.1 it is usually six. If your page fires off thirty requests at once the browser queues them. Six go out. The rest wait.

I ran into this on a project back in the days where a page needed to fetch data per item on the client. 😅

Some items just would not load and I could not figure out why. Turns out the requests were queuing and timing out. The real fix was not fighting the limit. The architecture was wrong. I moved the per-item data to the server and returned it all together with the items in one response.

If you are making a lot of parallel requests from the client you are probably doing something wrong one level up.

fetch Does Not Fail Like You Think

fetch does not reject on a 404. Or a 500. Or any HTTP error. It only rejects on network failures. Like the server is unreachable or DNS fails.

A 500 response is still a successful fetch as far as the browser is concerned. You got bytes back. If you are not checking response.ok you are silently swallowing errors.

localStorage Blocks the Main Thread

localStorage is synchronous. Every read and write blocks. It is not async. There is no callback. No promise. It just stops everything until the operation is done.

On a fast machine with a small store you will never notice. But if you are storing a lot of data or reading from it frequently it can cause jank. Especially on lower end devices. It’s generally never an issue, but good to be aware of.

Background Tabs Get Throttled

When a user switches to another tab the browser starts throttling your timers. setTimeout and setInterval get clamped to once per second at best. Some browsers throttle even harder after a few minutes. This is an issue I dealt with at Lovable where I couldn’t figure out why setTimeout wasn’t running when going to a different tab.

requestAnimationFrame just stops entirely.

If you are relying on precise timing for something in a background tab it will not work. WebSocket connections stay alive but your polling logic and animation loops basically freeze.

The 4ms Timer Clamp

Even in a foreground tab setTimeout(fn, 0) is not actually zero. After about four or five nested calls the browser clamps the minimum delay to 4ms. This is in the spec.

If you are using nested timeouts for some kind of tight loop you will hit a hard floor. It will never go faster than 4ms per tick. For truly fast async work use MessageChannel or queueMicrotask instead.

z-index Is Not What You Think

You set z-index: 9999 and the element is still behind something else. Everyone has been there.

z-index only works within a stacking context. Certain CSS properties create new stacking contexts. transform. opacity less than 1. filter. will-change. position: fixed.

Once an element creates a new stacking context its children can never escape it. Your z-index: 9999 only competes with siblings inside that same context. The parent itself might have a z-index of 1 and lose to something else entirely.

The Back-Forward Cache Will Break Your Assumptions

Modern browsers cache entire pages in memory when you navigate away. Hit the back button and the page is restored instantly. No reload. No re-render. JavaScript state is frozen and resumed.

This is called bfcache. It is great for performance. It also means your page load event listeners do not fire again. If you are setting up state in load or cleaning up in beforeunload things get weird.

Open WebSocket connections and unfinished fetch requests can prevent bfcache from working at all. Use the pageshow and pagehide events instead if you need to handle this correctly.

CSS Transitions Have a Cost You Cannot See

Animating width or height or top or margin forces the browser to recalculate layout. Every frame. That means it has to figure out where every other element on the page goes. Every frame.

Animating transform and opacity does not trigger layout. These properties get composited on the GPU. The browser barely breaks a sweat.

If your animations feel janky you are probably animating the wrong properties. Move things with transform: translateX() instead of left. Scale things with transform: scale() instead of changing width.

T

interesting read. practical approaches like this are way more useful than the theoretical overviews you usually find. bookmarking.

T

this was genuinely useful, I had no idea about the six connection limit and how much it affects loading on sites with lots of third-party scripts. explains why some of my client sites with Google Fonts + analytics + chat widgets load so much slower than they should. bookmarking this.