SWR refreshWhenHidden is false: a hidden tab stops polling Sume jobs

SWR stops polling in a hidden tab by default, but a Sume job keeps running and billing. Store the job id and resume the poll on focus instead of resubmitting.

5 min readSume
All posts

The answer

SWR's documented default is refreshWhenHidden = false: polling is skipped while the window is invisible, if refreshInterval is enabled. It also documents revalidateOnFocus = true, so when the tab comes back SWR revalidates. For a Sume job that is the right shape, with one rule: the job is not tied to the tab.

Sume's docs state that a client-side timeout does not cancel a job. It keeps running and still bills; you have only stopped watching. The same is true of a hidden tab, so keep the job id and read it again.

What the defaults do to a long render

A video or avatar-video job can run for minutes. If the user switches tabs after submitting, SWR pauses, the job continues, and on return the first revalidation reads the current state. Nothing is lost as long as the id is saved somewhere that survives a reload.

SWR defaults and what they mean for a long Sume job (read 2026-10-03)
SettingDefaultConsequence
refreshWhenHiddenfalseNo polling in a hidden tab
revalidateOnFocustrueA status read when the tab regains focus
refreshWhenOfflinefalseNo polling while offline
revalidateOnReconnecttrueA read when the connection returns

Persist the id, not the result

Write the job id to localStorage or your database right after the submit returns. On load, if an id is stored, mount the hook with it. The status route is cheap, and the response tells you whether the job is still running, finished, or ended in failed or canceled.

Do not resubmit because the page reloaded. A second submit is a second paid job unless you reuse the same Idempotency-Key with the same payload, which returns the original job. For refresh-safe flows, derive the key from your own record id and send it from your server.

// after the submit returns on your server route
localStorage.setItem("sume.job", submitted.data.request_id);

// on page load, pass the stored id to the polling hook
const id = localStorage.getItem("sume.job");
const { data } = useSumeJob(id);

Cancel is only possible early

If the user abandons the page, you can cancel through the job's cancel URL, but only while cancelable is true. Sume's reference says cancel works before generation starts; afterwards it answers 409 job_generation_already_started. So leaving a job to finish and showing it on the next visit is usually the cheaper outcome than trying to cancel late.

A resume checklist

When the hook remounts with a stored id, handle each status explicitly: queued and processing keep the poll going, completed fetches the result and clears the stored id, and failed or canceled show the job's error and clear it as well. Treat an unknown id (404) as a stale record and drop it.

Because SWR revalidates on focus and on reconnect, a laptop that sleeps through a render wakes up to a fresh status read instead of a stale spinner. You do not need extra timers for that; the defaults already cover it. What you do need is the id, which is why the storage line comes before everything else.

If the user must not leave work unattended, a webhook to your server removes the tab from the picture entirely: the terminal event updates your database, and the page simply reads it.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume