Coordinate browser tabs with Web Locks
A Web Locks coordinator gives same-origin tabs explicit ownership, bounded waiting, cancellation, leader duties, observability, and safe fallbacks.
Opening a second tab creates another application runtime, not another view of the same JavaScript state.
Both tabs can believe they should refresh a credential, drain an offline queue, migrate IndexedDB, or own a live connection. Local storage events and timestamps can communicate, but they do not provide mutual exclusion without races and stale-owner problems.
The Web Locks API specification defines origin-scoped named locks, exclusive and shared modes, queued requests, optional immediate acquisition, cancellation, and lock-manager inspection.
We will wrap that primitive in a product-aware coordinator: name the protected resource, keep critical sections bounded, expose passive-tab state, recover after page death, and retain an idempotent operation underneath.
A browser lock coordinates cooperating clients; it does not turn one tab into a permanent server.
Tabs and workers request `queue-drain:v2`, `token-refresh:user`, or another stable origin-local boundary.
The granted callback owns exclusive or shared authority until its returned promise settles or the execution context disappears.
Broadcast status or durable state lets waiting tabs render progress and re-read truth after the lock is released.
Name the protected resource
Lock names should correspond to the invariant or shared browser resource, include schema scope when needed, and avoid one global lock that serializes unrelated work.
I would pressure-test that decision with four questions:
- What state can conflict?
- Is the scope user or database?
- Can independent tasks proceed?
- Does a version change semantics?
The failure mode here is using app as the name for every coordinated task. In multi-tab web applications where several documents or workers can refresh tokens, replay an offline queue, run a migration, maintain a socket, compact storage, elect a leader, or write the same shared browser resource concurrently, that can hide the exact boundary a reviewer or teammate needs to understand. My working artifact would be a lock-name registry. I want it close enough to the implementation that it can change the work, not created afterward to decorate the story.
The result I would look for is minimal contention around the actual shared boundary. That is a narrower claim than saying the whole system improved, but it is also one I can verify and defend.
In practice, I would put a lock-name registry beside the question “What state can conflict?” before the first implementation review. The next pass would use “Is the scope user or database?” to test the boundary, then “Can independent tasks proceed?” to expose the state most likely to be missed. I would keep “Does a version change semantics?” for the release check because it asks whether the decision still holds outside the ideal path. The work is ready to move when the artifact can explain the choice and the observed result supports minimal contention around the actual shared boundary.
Choose exclusive or shared mode
Most mutations need exclusive ownership, while shared mode is useful only when concurrent readers are safe and writers can wait without starvation.
The practical review starts here:
- Can two holders coexist?
- What invariant protects writes?
- How long can readers hold?
- Does an exclusive request need priority?
Those questions keep using shared mode because the work feels read-heavy from becoming the default. I would capture the decision in a lock-mode decision table, then use it while the work is still cheap to change. For coordinated browser work across tabs and workers, the artifact should make ownership, constraint, and next action visible without requiring a private explanation.
Success would look like mode semantics aligned with the data transition. If I cannot point to that evidence, I have a direction, not a finished decision.
The implementation move is to make a lock-mode decision table part of the working surface. I would use it to answer “Can two holders coexist?” while scope is still flexible, and “What invariant protects writes?” before code or content becomes expensive to unwind. During QA, “How long can readers hold?” and “Does an exclusive request need priority?” become concrete checks rather than discussion prompts. That sequence turns coordinated browser work across tabs and workers into something the team can operate and gives me a specific outcome to report: mode semantics aligned with the data transition.
- CandidateTab requests leadership
Acquisition carries a cancellation signal and timeout; hidden tabs can decline while visible tabs remain candidates.
- LeaderCallback owns the lock
Perform heartbeat-free bounded work, publish identity for UI only, and make each external effect idempotent.
- SuccessorContext closes; queue advances
The browser releases the lock when the client dies; another tab acquires and reconciles durable state before continuing.
Bound the critical section
A lock callback should contain only the work requiring exclusion and should never wait for open-ended user input, a hidden dialog, or an unrelated request.
Before implementation, I would answer:
- Which statements need authority?
- Can preparation happen before grant?
- What is the maximum duration?
- Can finalization happen after release?
The artifact is a critical-section timing budget. Its job is to expose the tradeoff early enough that design, engineering, support, or product can disagree with something concrete. The common trap is holding leadership for the entire lifetime of a tab; it moves uncertainty downstream and makes the final interface carry a problem the system never resolved.
For me, the useful receipt is fairer acquisition and faster recovery. That connects an origin-scoped named lock with explicit ownership, bounded acquisition, cancellation, lease-like product state, fallback behavior, and idempotent recovery to an observable result instead of a process claim.
I would test this with one typical case and one boundary case. The typical case should make “Which statements need authority?” easy to answer. The boundary should force a decision about “Can preparation happen before grant?” and “What is the maximum duration?.” I would record both in a critical-section timing budget, including the part that stayed unresolved after the first pass. The final check, “Can finalization happen after release?,” is where the artifact earns its place: it either supports fairer acquisition and faster recovery, or it shows exactly why another iteration is needed.
Cancel queued acquisition
An AbortSignal should remove requests that no longer matter after navigation, sign-out, timeout, visibility policy, or superseding user intent.
I would use these prompts during the working review:
- When does waiting become stale?
- Which reason cancelled it?
- Does cancellation stop active work?
- What UI state follows?
If the team slips into leaving abandoned tabs queued for authority, the product can still look complete while its operating rule stays ambiguous. I would make an acquisition cancellation contract the shared reference and keep it small enough to update as evidence changes.
The standard is lock demand that reflects current product intent. That tells me whether the decision helped the product, not merely whether the document was completed.
The working sequence is small: draft an acquisition cancellation contract, review it against “When does waiting become stale?,” implement the narrowest useful path, and then return with evidence for “Which reason cancelled it?.” I would use “Does cancellation stop active work?” to inspect product consequence and “What UI state follows?” to decide whether the result is stable enough to ship. This keeps leaving abandoned tabs queued for authority visible as a known risk and makes lock demand that reflects current product intent the release receipt rather than a hopeful conclusion.
| Signal | Decision | Working note |
|---|---|---|
| exclusive | One writer | Use for queue drain, token refresh, migration, or any invariant where concurrent execution would conflict. |
| shared | Several readers | Multiple clients may hold together while an exclusive writer waits; fairness and critical-section duration still matter. |
| ifAvailable | Try without waiting | A tab can remain passive immediately, but should observe shared state and know when another attempt becomes useful. |
Use immediate acquisition deliberately
ifAvailable supports passive-tab behavior without blocking, but the application still needs a trigger or observation path for trying again later.
I would pressure-test that decision with four questions:
- May this tab remain passive?
- How does it learn progress?
- When should it retry?
- Can all tabs decline forever?
The failure mode here is polling ifAvailable continuously until it wins. In multi-tab web applications where several documents or workers can refresh tokens, replay an offline queue, run a migration, maintain a socket, compact storage, elect a leader, or write the same shared browser resource concurrently, that can hide the exact boundary a reviewer or teammate needs to understand. My working artifact would be an active/passive tab state machine. I want it close enough to the implementation that it can change the work, not created afterward to decorate the story.
The result I would look for is quiet coordination with bounded acquisition attempts. That is a narrower claim than saying the whole system improved, but it is also one I can verify and defend.
In practice, I would put an active/passive tab state machine beside the question “May this tab remain passive?” before the first implementation review. The next pass would use “How does it learn progress?” to test the boundary, then “When should it retry?” to expose the state most likely to be missed. I would keep “Can all tabs decline forever?” for the release check because it asks whether the decision still holds outside the ideal path. The work is ready to move when the artifact can explain the choice and the observed result supports quiet coordination with bounded acquisition attempts.
Publish state outside the lock
BroadcastChannel or storage events can announce leader identity and progress for the interface, while durable state remains the source of truth after missed messages.
The practical review starts here:
- Which state is only advisory?
- Can a new tab reconstruct progress?
- Does the message expose private data?
- What happens when channels close?
Those questions keep treating the last heartbeat message as durable authority from becoming the default. I would capture the decision in a cross-tab status message schema, then use it while the work is still cheap to change. For coordinated browser work across tabs and workers, the artifact should make ownership, constraint, and next action visible without requiring a private explanation.
Success would look like responsive passive tabs without correctness depending on delivery. If I cannot point to that evidence, I have a direction, not a finished decision.
The implementation move is to make a cross-tab status message schema part of the working surface. I would use it to answer “Which state is only advisory?” while scope is still flexible, and “Can a new tab reconstruct progress?” before code or content becomes expensive to unwind. During QA, “Does the message expose private data?” and “What happens when channels close?” become concrete checks rather than discussion prompts. That sequence turns coordinated browser work across tabs and workers into something the team can operate and gives me a specific outcome to report: responsive passive tabs without correctness depending on delivery.
Design for client death
Tabs freeze, crash, navigate, and are discarded; successors should assume the prior callback stopped at any await and reconcile before resuming.
Before implementation, I would answer:
- Which effects may be partial?
- Where is the durable cursor?
- Can the successor repeat safely?
- Does page suspension change timers?
The artifact is a successor reconciliation procedure. Its job is to expose the tradeoff early enough that design, engineering, support, or product can disagree with something concrete. The common trap is assuming finally always runs before a tab disappears; it moves uncertainty downstream and makes the final interface carry a problem the system never resolved.
For me, the useful receipt is recovery without permanent stale leadership. That connects an origin-scoped named lock with explicit ownership, bounded acquisition, cancellation, lease-like product state, fallback behavior, and idempotent recovery to an observable result instead of a process claim.
I would test this with one typical case and one boundary case. The typical case should make “Which effects may be partial?” easy to answer. The boundary should force a decision about “Where is the durable cursor?” and “Can the successor repeat safely?.” I would record both in a successor reconciliation procedure, including the part that stayed unresolved after the first pass. The final check, “Does page suspension change timers?,” is where the artifact earns its place: it either supports recovery without permanent stale leadership, or it shows exactly why another iteration is needed.
Keep underlying operations idempotent
The Web Locks API coordinates cooperative same-origin clients but cannot prevent server workers, old browsers, bugs, or external processes from repeating the transition.
I would use these prompts during the working review:
- What other writers exist?
- Can a lock be bypassed?
- Which idempotency key survives?
- Does the database enforce the invariant?
If the team slips into using the browser lock as the only duplication defense, the product can still look complete while its operating rule stays ambiguous. I would make an end-to-end correctness boundary the shared reference and keep it small enough to update as evidence changes.
The standard is safe outcomes even outside the cooperative client set. That tells me whether the decision helped the product, not merely whether the document was completed.
The working sequence is small: draft an end-to-end correctness boundary, review it against “What other writers exist?,” implement the narrowest useful path, and then return with evidence for “Can a lock be bypassed?.” I would use “Which idempotency key survives?” to inspect product consequence and “Does the database enforce the invariant?” to decide whether the result is stable enough to ship. This keeps using the browser lock as the only duplication defense visible as a known risk and makes safe outcomes even outside the cooperative client set the release receipt rather than a hopeful conclusion.
Provide progressive fallback
Feature detection should select server coordination, IndexedDB claims, BroadcastChannel election, or duplicated-safe work according to consequence—not an unreviewed localStorage spinlock.
I would pressure-test that decision with four questions:
- Which browsers lack support?
- Can work move to the server?
- Is duplicate execution harmless?
- How is reduced behavior disclosed?
The failure mode here is inventing timestamp leases without handling clock and suspension. In multi-tab web applications where several documents or workers can refresh tokens, replay an offline queue, run a migration, maintain a socket, compact storage, elect a leader, or write the same shared browser resource concurrently, that can hide the exact boundary a reviewer or teammate needs to understand. My working artifact would be a capability and fallback matrix. I want it close enough to the implementation that it can change the work, not created afterward to decorate the story.
The result I would look for is bounded behavior across supported clients. That is a narrower claim than saying the whole system improved, but it is also one I can verify and defend.
In practice, I would put a capability and fallback matrix beside the question “Which browsers lack support?” before the first implementation review. The next pass would use “Can work move to the server?” to test the boundary, then “Is duplicate execution harmless?” to expose the state most likely to be missed. I would keep “How is reduced behavior disclosed?” for the release check because it asks whether the decision still holds outside the ideal path. The work is ready to move when the artifact can explain the choice and the observed result supports bounded behavior across supported clients.
Test multiple real contexts
QA should open tabs and workers, race acquisition, abort queues, close leaders mid-await, freeze pages, change visibility, test shared/exclusive order, and inspect durable results.
The practical review starts here:
- Can races start together?
- Does closure release authority?
- Can stale messages mislead?
- Does fallback preserve the invariant?
Those questions keep mocking navigator.locks inside one JavaScript realm from becoming the default. I would capture the decision in a multi-context coordination harness, then use it while the work is still cheap to change. For coordinated browser work across tabs and workers, the artifact should make ownership, constraint, and next action visible without requiring a private explanation.
Success would look like evidence from the concurrency the feature actually coordinates. If I cannot point to that evidence, I have a direction, not a finished decision.
The implementation move is to make a multi-context coordination harness part of the working surface. I would use it to answer “Can races start together?” while scope is still flexible, and “Does closure release authority?” before code or content becomes expensive to unwind. During QA, “Can stale messages mislead?” and “Does fallback preserve the invariant?” become concrete checks rather than discussion prompts. That sequence turns coordinated browser work across tabs and workers into something the team can operate and gives me a specific outcome to report: evidence from the concurrency the feature actually coordinates.
What I would show in the work
The public version needs evidence from the work itself. For this topic, the first five artifacts I would reach for are:
- a lock-name registry
- a lock-mode decision table
- a critical-section timing budget
- an acquisition cancellation contract
- an active/passive tab state machine
I would not publish all five at equal weight. One should orient the reader, one should reveal the hardest tradeoff, and one should prove the result. The others can live in a downloadable note or appear as supporting frames. That edit matters because an origin-scoped named lock with explicit ownership, bounded acquisition, cancellation, lease-like product state, fallback behavior, and idempotent recovery becomes harder to understand when every process detail is treated as equally important.
I would also show one rejected direction. The useful version is specific: which option looked attractive, which constraint made it wrong, and what evidence supported the narrower choice. That gives an engineering manager something real to question and keeps the case study from reading like the final answer was obvious from the beginning.
# acquire locks.request(name, { signal }, run) Abort while queued, keep the callback small, never await unrelated user interaction, and expose waiting/leader/passive state.
# operate claimNextBatch(operationId) Server or IndexedDB transition remains idempotent and versioned; the browser lock reduces contention but is not the final correctness guard.
# recover read durable cursor after grant A successor ignores stale BroadcastChannel presence, reconciles committed work, resumes from truth, and emits a new leader receipt.
Resource path
The practical follow-up I would build is a Web Locks coordination starter with named resources, exclusive and shared modes, AbortSignal acquisition, ifAvailable path, leader loop, page-lifecycle handling, BroadcastChannel status, operation idempotency, feature detection, fallback adapter, observability, and cross-tab tests. I am treating that as a resource backlog item, not pretending the adjacent downloads below are the same artifact. The related cards cover useful pieces of the workflow today; this specific file should only be published when its examples, fields, and instructions are complete.
The first version should stay concise: context, constraint, decision, evidence, owner, and follow-up. Its value would come from helping someone repeat this exact review, not from adding another generic PDF to the site.
Review checklist
The article-specific review questions are:
- What state can conflict?
- Can two holders coexist?
- Which statements need authority?
- When does waiting become stale?
- May this tab remain passive?
- Which state is only advisory?
- Which effects may be partial?
- What other writers exist?
- Which browsers lack support?
- Can races start together?
I would add two editorial checks before publishing: can a recruiter find the point in the first minute, and can an engineer trace at least one claim to an implementation or production receipt? If either answer is no, the article needs another edit.
Implementation notes
For coordinated browser work across tabs and workers, I would write the implementation note before polish. It would name the changed surface, source of truth, owner, failure boundary, and verification path. Those details prevent the principle from floating above the actual code or operational workflow.
The proof signals I care about are specific to this article:
- responsive passive tabs without correctness depending on delivery
- recovery without permanent stale leadership
- safe outcomes even outside the cooperative client set
- bounded behavior across supported clients
- evidence from the concurrency the feature actually coordinates
I would choose two or three of those signals for the first release rather than instrumenting everything. The strongest pair usually combines one direct behavior check with one operating check: a route and a data query, a keyboard path and a support state, a handler replay and a reconciliation result, or a migration count and a rendered screen.
The follow-up belongs in the note before shipping. It should say what remains temporary, what evidence would trigger another pass, and who owns that decision. That is how the first version stays intentionally narrow without making the boundary invisible.
Case-study packaging
I would structure the case-study version around the four visual lessons already established:
- Every tab asks one origin lock manager for authority.
- Leader work should tolerate abrupt browser lifecycle changes.
- Acquisition options encode different product behavior.
- A coordinator keeps the lock and business operation separate.
The opening frame explains the product pressure. The middle two show the decision moving through the system. The last frame is the receipt: what was checked, what held, and what remained unresolved. That order lets the reader move from product judgment into implementation detail without reconstructing the whole project first.
I would include one caveat tied to multi-tab web applications where several documents or workers can refresh tokens, replay an offline queue, run a migration, maintain a socket, compact storage, elect a leader, or write the same shared browser resource concurrently: a data limit, rollout boundary, unsupported state, external dependency, or result that is still directional. A precise caveat makes the evidence easier to trust because it shows where the claim stops.
The final test is whether the page creates a better conversation. If the artifact helps someone ask a sharper question about product judgment, implementation detail, or release proof in a live interview, it belongs in the story.
Interview angle
In an interview, I would explain this through an origin-scoped named lock with explicit ownership, bounded acquisition, cancellation, lease-like product state, fallback behavior, and idempotent recovery. The story should start with the product pressure, then move into the system constraint, the artifact, and the proof. That order keeps the answer grounded. It also gives the interviewer several places to go deeper: data, frontend architecture, design systems, support, migration, accessibility, or release process.
The strongest version of the answer includes a tradeoff. I want to be able to say what I chose, what I left alone, and how I knew the work helped. That is more credible than presenting every project as a clean win.
The hiring signal
A Web Locks coordination layer is a hiring signal because it shows I can reason about concurrency, browser lifecycle, progressive enhancement, and user-visible ownership inside one origin.
That is the level I want this site to communicate. The work should show taste, but it should also show operating judgment. It should make me look like someone who can enter a real product system, understand the messy middle, ship the useful version, and leave enough proof for the next person to trust it.
Use this after reading.
Practical downloads and templates that turn the article into something you can bring into a product review, implementation pass, or agent workflow.
Front-End State Recipes
Reusable recipes for optimistic actions, loading, empty, error, data-transition, and disabled-control states.
Agent-Ready API Spec Template
An OpenAPI and Postman starter template for APIs that AI agents can discover, call, and recover from safely.
UI PR Risk Review Checklist
A merge-readiness checklist for product intent, states, accessibility, visual durability, and UI implementation risk.