Blog / The '5-minute task'

War stories

The '5-minute task' that lost a fight with the browser

A trivial window.open() ticket, one innocent await before it, and the browser policy that ended up killing the whole feature.

Aug 2026 · 4 min read

Every developer knows the most dangerous phrase in software: "it's a 5-minute task."

Here's mine.

The task

At some point in the user flow: user clicks a button → open a new tab → continue there. That's it. That's the whole ticket.

const handleClick = () => {
  window.open(url);
};

Ship it. Works everywhere. Well — "works in Safari" is always a bold claim, but even Safari behaved. I mentally closed the ticket and moved on with my life.

✅ THE HAPPY TIMES

[ user click ] ──► window.open(url) ──► 🗔 new tab opens

Then came The Change Request

"Small thing: before opening the new tab, fire an HTTP request, wait for the response, and then open the tab."

Sure. Small thing. One await, what could possibly go wrong:

const handleClick = async () => {
  await fetch("/api/track-the-thing");
  window.open(url); // 🚫 blocked
};

And that's where the browser said: no.

❌ AFTER THE CHANGE REQUEST

[ user click ] ──► fetch() ──► ⏳ await ──► window.open(url) ──► 🚫 POPUP BLOCKED
                                  │
                                  └── user activation expired here

Why browsers hate this

Turns out this is not a bug — it's policy. Browsers only allow window.open() as a direct result of a user action. The click gives you a short-lived "user activation" token, and the moment you go async and wait for a network response, that trust window slams shut. From the browser's point of view, a tab that opens after some background activity is indistinguishable from popup spam from 2005.

So the rule is brutally simple:

click ──► window.open        ✅ works
click ──► anything ──► open  ❌ doesn't

(Safari, as the strictest bouncer at this club, enforces it with particular enthusiasm.)

The workaround graveyard

I honestly don't remember every hack I tried — it's a blur of tab-related grief. There are known tricks (like opening a blank tab synchronously on click and redirecting it once the response arrives), but every single workaround failed the most important review of all: the business said no. Blank tabs flashing at users apparently don't scream "premium product experience."

So we did the mature engineering thing: abandoned the idea entirely and redesigned the flow to not need a new tab at that point.

Sometimes the best fix for a browser restriction is to stop arguing with the browser.

👉 What's your "5-minute task" that turned into a multi-day negotiation with browser policies?

Fought the popup blocker and lost?

Share your story — especially your own "5-minute task" that spiraled. I need to know I'm not alone in this support group.