TabLeaderPlugin
If your app can be open in multiple tabs at the same time, you have probably seen two tabs fighting over audio.
TabLeaderPlugin stops that by using the Web Locks API to elect one tab as the leader.
Only the leader tab is allowed to play.
When a second tab acquires the lock, the first tab loses the lock; the onLost action handler is registered but the involuntary-loss signal is not yet emitted in this release.
If the leader tab is closed or navigated away, the browser hands the lock to the next waiting tab automatically.
This plugin is a direct re-export of the player core’s TabLeaderPlugin.
The music player does not override any behaviour.
import nmMPlayer from '@nomercy-entertainment/nomercy-music-player';
import { TabLeaderPlugin } from '@nomercy-entertainment/nomercy-music-player/plugins';
import type { TabLeaderOptions } from '@nomercy-entertainment/nomercy-music-player/plugins';
Plugin id: 'tab-leader'
What it does
On use(), the plugin calls navigator.locks.request(key, callback).
The browser gives the lock to the first tab that asks.
That tab emits leader-acquired and is free to play.
Every other tab queues behind it.
When the leader releases the lock (or the tab closes), the browser fires the next tab’s callback and it emits leader-acquired in turn.
In environments where navigator.locks is absent, the plugin emits unsupported and becomes a no-op.
Options
| Option | Type | Default | Description |
|---|---|---|---|
onLost | 'pause' | 'mute' | 'pause' | Action when this tab releases the lock voluntarily (releaseLock() / dispose()): 'pause' pauses, 'mute' mutes. Not yet triggered on the involuntary path where another tab steals the lock. |
handoffOnVisible | boolean | true | Re-request the lock whenever the tab becomes visible via document.visibilitychange. The behaviour is active when this option is absent; set it explicitly to false to disable. |
getLockKey | () => string | undefined | Factory returning the lock key. When absent, the plugin falls back to the shared key 'nomercy-player-leader', which is global across all instances on the same origin. Return a per-player or per-session string to scope leadership more narrowly. |
Events
| Event | Payload | Description |
|---|---|---|
leader-acquired | void | Fired when this tab successfully acquires the leader lock. |
leader-released | void | Fired when this tab voluntarily releases the lock via releaseLock() or on dispose. |
unsupported | void | Fired when the Web Locks API is unavailable. The plugin is a no-op in this case. |
Listen using the namespaced string form:
player.on('plugin:tab-leader:leader-acquired', () => {
console.log('This tab is now the leader');
});
player.on('plugin:tab-leader:unsupported', () => {
console.warn('Web Locks not available; tab coordination disabled');
});
Methods
isLeader()
isLeader(): boolean
Returns true when this tab currently holds the leader lock.
requestLock()
requestLock(): Promise<void>
Request the leader lock.
The returned promise resolves when the lock is voluntarily released via releaseLock() or when the plugin disposes — not at acquire time.
Calling while an election is already in progress returns the existing promise rather than starting a second election.
Emits leader-acquired when the lock is granted.
releaseLock()
releaseLock(): void
Voluntarily release the leader lock so the next waiting tab can take over.
Emits leader-released when this tab was the leader.
No-ops when this tab is not the current leader.
requestLeadership()
requestLeadership(): Promise<boolean>
Backwards-compatible alias for requestLock().
Returns true when leadership is held after the attempt resolves, false otherwise.
releaseLeadership()
releaseLeadership(): void
Backwards-compatible alias for releaseLock().
Registration
Basic registration; the plugin calls requestLock() automatically on use():
import nmMPlayer from '@nomercy-entertainment/nomercy-music-player';
import { TabLeaderPlugin } from '@nomercy-entertainment/nomercy-music-player/plugins';
const player = nmMPlayer('main')
.addPlugin(TabLeaderPlugin)
.setup({ playlist: myTracks });
With options:
import nmMPlayer from '@nomercy-entertainment/nomercy-music-player';
import { TabLeaderPlugin } from '@nomercy-entertainment/nomercy-music-player/plugins';
const player = nmMPlayer('main')
.addPlugin(TabLeaderPlugin, {
onLost: 'pause',
handoffOnVisible: true,
getLockKey: () => `nomercy-leader-${location.hostname}`,
})
.setup({ playlist: myTracks });
const leaderPlugin = player.getPlugin(TabLeaderPlugin)!;
player.on('play', () => {
if (!leaderPlugin.isLeader()) {
void player.pause?.();
}
});
Browser support
Web Locks is supported in Chrome 69+, Firefox 96+, and Safari 15.4+.
In environments without navigator.locks, the plugin emits unsupported and becomes a no-op.
All tabs can play simultaneously without error in those environments.
See also
- Player Core, TabLeaderPlugin, full player core plugin documentation
- Plugin registration, how
addPluginandgetPluginwork - Event system, namespaced plugin events