cover
ReactFrontenddev-toolsAPI

My Girl Said “I’m Ready” and Then Made Me Wait 23 Minutes, APIs do the same thing

Cover
Screenshot_20260105_005009.png
Slug
browser-timing-tab-queued-requests-performance
Published
Published
Date
Jan 4, 2026
Category
React
Frontend
dev-tools
API
My girl said, “I’m ready, let’s go.”
So I did what any reasonable person would do.
I sat down.
Relaxed.
Waited.
Five minutes passed.
Ten minutes passed.
Twenty-three minutes passed.
Turns out… she wasn’t ready.
She was stalling.
And that’s exactly how browsers treat your API requests.
Your backend might be ready.
Your frontend might be ready.
But Chrome? Chrome might just be sitting there like:
“Yeah… hold on. Not yet.”

The Tab Everyone Pretends Doesn’t Exist

Let’s be honest most debugging sessions look like this:
  • Console → checking for red errors ✅
  • Network → checking request status and response times ✅
  • Elements → maybe poking around ❓
  • Timingwait… what’s that?
Even when developers do open Network, they usually stop at:
  • Headers
  • Payload
  • Response
Meanwhile, Timing sits right there, quietly exposing the real reason your app feels slow.
notion image

That Tiny “Timing” Tab You’ve Been Ignoring

This view lives inside Chrome DevTools → Network, after you click a request, right next to:
  • Headers
  • Payload
  • Preview
  • Response
  • Initiator
  • Timing ← this one
Most developers never make it past “Response.” That’s a mistake.
Because the Timing tab doesn’t tell you what the server returned it tells you how long the browser made your request wait before it even got a chance.
Now look closely at the screenshot.

What’s Actually Happening Here

From the Timing breakdown:
  • Queueing: ~5.05s
  • Stalled: ~7.65s
  • Waiting for server response: 10.22s
  • Content download: ~1.78ms
Total time before the frontend gets usable data:
👉 22.92 seconds
Here’s the punchline:
The server only needed 10.22 seconds. The page waited 22.92 seconds before showing the main data.
That means more than half the time was spent doing absolutely nothing just waiting for the browser to allow the request to proceed.

Queueing vs Stalled (In Human Terms)

  • Queueing The browser wanted to send the request… but couldn’t. All available connection slots for that domain were already taken.
  • Stalled A slot finally opened up, but the browser still had to wait on internal scheduling, connection reuse, or other in-flight requests.
None of this involves your backend. None of this shows up in server logs. None of this throws an error.
From the server’s point of view, this request was fast.
From the user’s point of view, your app felt broken.

Why This Screenshot Is So Dangerous (And So Useful)

If you only looked at:
  • the Response tab → “server is slow-ish”
  • backend logs → “everything looks normal”
You’d miss the real issue completely.
The Timing tab exposes the uncomfortable truth:
Your app wasn’t slow because the server took too long. It was slow because the browser made the request wait in line.

The Browser Is Throttling You, And It’s Not Asking Permission

Browsers enforce a maximum number of concurrent connections per origin.
For most modern browsers, that limit is roughly:
~6 simultaneous requests per domain
Once that limit is hit:
  • New requests are queued
  • They don’t reach the server
  • They don’t show up in backend logs
  • They just… wait
This happens before JavaScript errors, before your API, and before any monitoring tools notice.

Yes, You’ve Written This Code Don’t Lie

Imagine a dashboard page that loads the following on initial render:
  • User profile
  • Organization settings
  • Permissions
  • Feature toggles
  • Notifications count
  • Sidebar navigation config
  • Main dashboard data (critical)
All fired at once.
All sent to the same API domain.
All triggered by various useEffect hooks because “they’re independent.”
The browser looks at this and says:
“Cool. I’ll take the first six.
The rest can wait.”
Guess which request often gets queued?
Yep the one the page actually depends on.

Why Everything Looks Fine Until You Look Here

Dev trying to get to the root cause
Dev trying to get to the root cause
From a developer’s point of view:
  • APIs respond fast when tested alone
  • Backend logs show low latency
  • No errors in Console
  • No failed requests in Network
But in the Timing tab you see something like:
  • Queueing: 6–8 seconds
  • Request sent: microseconds
  • Waiting (server): 20ms
  • Download: 1ms
That’s when it clicks.

Why the Browser Doesn’t Care Which Request Is “Important”

From the browser’s perspective, these are all equal:
fetch('/profile') fetch('/settings') fetch('/permissions') fetch('/features') fetch('/notifications') fetch('/navigation') fetch('/dashboard-data')
Same origin.
Same priority.
Same timing.
The browser doesn’t understand your business logic.
It only understands connection slots.
Requests are allowed through in the order they’re initiated, not by importance.

The Real Bug Is “Let’s Just Fetch Everything”

This isn’t about “too many requests.”
It’s about:
  • Too many requests
  • At the same time
  • Competing for limited browser connections
This pattern is extremely common in:
  • React apps
  • Hook-based data fetching
  • “Load everything on mount” designs
🔥 Hot take
If your page needs 6+ API calls at the exact same time just to become usable, the problem isn’t performance — it’s architecture, and the way how you are writing code.

Fixing It by Designing for Zero Queued Requests

The real fix is not “optimize the slow request.”
The real fix is to design your system so that the browser never has a reason to queue a critical request at all.

How to Debug This in Practice

Next time someone says “the app feels slow”, do this:
  1. Open DevTools → Network
  1. Click the slow request
  1. Open the Timing tab (next to Headers / Payload / Response)
  1. Look at:
      • Queueing time
      • Waiting time
If Queueing > 3–4ms, you’ve found the culprit.
notion image

Common Mistakes to Avoid

  • ❌ Treating all requests as equal
  • ❌ Firing all requests on component mount
  • ❌ Assuming no errors = good performance
  • ❌ Ignoring the Timing tab entirely
Performance bugs don’t always throw stack traces.
Some just quietly waste seconds.

Key Takeaways (The Stuff Worth Remembering)

  • The Timing tab lives inside Network and it’s critical
  • Queueing means the browser hasn’t sent the request yet
  • Browsers limit concurrent connections per domain
  • Too many parallel requests introduce hidden latency
  • Better sequencing beats aggressive preloading
  • The real fix is to design your data fetching so critical requests never have to wait in the queue

Wrapping It Up (Design So Nothing Queues)

Your app wasn’t broken.
Your backend wasn’t slow.
Your code wasn’t “wrong” in the traditional sense.
The problem was architectural: you let the browser treat every request as equally important and equally urgent.
Once you:
  • Define a clear critical path
  • Fire that path with minimal competition
  • Batch and delay non-essential calls
  • Centralize orchestration instead of sprinkling fetch everywhere
…you stop “optimizing slow requests” and start designing a system where important requests never get queued in the first place.
So next time performance feels off, don’t just ask “how do I make this request faster?”
Ask:
“Why did the browser have to queue this at all and how can I design things so it never needs to?”
That’s where the real performance win lives, long before the request ever shows up in your logs.

Related Posts