SharedWorker in JavaScript
A SharedWorker is a JavaScript thread that several contexts (tabs, windows, iframes) can use at the same time, as long as they are loaded from the same origin (protocol plus host plus port). Unlike a regular Worker, which dies when its tab is closed, a SharedWorker stays alive while at least one window is still connected.
Theory
TL;DR
- One thread serves every tab and iframe of a single origin.
- It is created with
new SharedWorker('shared-worker.js'), and the channel isshared.port. - Every new connection arrives in the worker as an
onconnectevent carrying its ownMessagePort. - The port has to be activated with
port.start()if you subscribe withaddEventListenerinstead ofonmessage. - It lives while at least one client is connected, then the browser shuts it down.
- There is no DOM access, and data is passed as a copy through structured clone.
Quick example
The code in shared-worker.js:
// shared-worker.js
onconnect = (event) => {
const port = event.ports[0];
console.log('New connection');
port.postMessage('Hello, tab!');
port.onmessage = (e) => {
console.log('Message from a tab:', e.data);
// send a reply back
port.postMessage(`Received: ${e.data}`);
};
};The code on every tab (main.js):
// main.js
const shared = new SharedWorker('shared-worker.js');
const port = shared.port;
// activate the channel
port.start();
// listen for replies from the worker
port.onmessage = (e) => {
console.log('SharedWorker reply:', e.data);
};
// send a message to the worker
port.postMessage('Hello from a tab!');What happens:
- all tabs connect to one and the same worker;
- each of them receives messages through its own
port; - the worker can coordinate them, for example broadcasting shared data to everyone.
How SharedWorker differs from a regular Worker
| Feature | Worker (Dedicated) | SharedWorker |
|---|---|---|
| Used by | One page | Several tabs and windows |
| Created with | new Worker('worker.js') | new SharedWorker('worker.js') |
| Communication channel | postMessage directly | Through a port object |
| Lifetime | While the tab is open | While at least one context is connected |
| Reachable from other tabs | No | Yes, on the same origin |
The interaction diagram:
[ Tab 1 ] [ Tab 2 ] [ Iframe ]
| | |
+--------> [ SharedWorker Thread ] <+
^ |
| (ports[])
v
business logicEvery tab connects to one worker thread, and the worker exchanges messages with each tab through that tab's port.
How onconnect and MessagePort work
When a new tab calls new SharedWorker(), the browser does not spawn a new thread if the worker already exists. It simply fires the onconnect event and passes in a MessagePort object, which is the communication channel.
That way a single SharedWorker can manage dozens of ports, meaning dozens of tabs.
Broadcasting to every tab
// shared-worker.js
const ports = [];
onconnect = (e) => {
const port = e.ports[0];
ports.push(port);
port.start();
port.onmessage = (event) => {
// broadcast the message to every connected tab
for (const p of ports) {
p.postMessage(`From a tab: ${event.data}`);
}
};
};Now, if you open two tabs:
- tab 1 sends
port.postMessage("Hi"), - both tabs receive
"From a tab: Hi".
That is already a real chat between tabs.
Where it is useful
| Scenario | Why SharedWorker helps |
|---|---|
| Shared chat between tabs | messages can be synchronised |
| Background data processing | one tab computes, everyone gets the result |
| A shared WebSocket subscription | one connection for all tabs, saving resources |
| Shared cache and IndexedDB logic | a single background for all of them |
| State synchronisation | for example a shared auth token across tabs |
What to keep in mind
- SharedWorker works only within one origin (
https://example.comis not the same ashttp://example.com). - It is not a Service Worker; the worker types are different and not interchangeable.
- Support is incomplete: some older browsers and Safari on iOS do not have it.
- Communication goes through
port.postMessage()andport.onmessage.
Security and data transfer
- Workers are isolated: no access to the DOM,
windowordocument. - Data is passed by copy (structured clone).
- For fast exchange of large binary payloads you can use Transferable objects (
ArrayBuffer).
A simple analogy:
Imagine a SharedWorker is the office server in a company. Every employee (a tab) can connect to it. The server keeps the shared data and pushes updates to everyone. While at least one employee is working, the server stays up. When everyone closes their tabs, the server shuts down automatically.
Summary:
| Feature | Description |
|---|---|
| What it does | Lets several pages of one site share a JS thread |
| Main advantage | Shared logic and data across tabs |
| Communication | Through a MessagePort (port.postMessage / port.onmessage) |
| Lifecycle | While at least one client is open |
| No DOM access | Messages only |
| Typical scenarios | Shared WebSocket, cache, state sync, cross tab chat |
Common mistakes
- Forgetting
port.start(). If you subscribe withport.addEventListener('message', ...), the port stays inactive and no messages arrive. Assigningport.onmessagestarts the port implicitly. - Calling
shared.postMessage(...)instead ofshared.port.postMessage(...). In a SharedWorker the method lives on the port, not on the worker object itself. - Expecting a SharedWorker to see a tab from a different origin or a different script path. A worker is identified by origin plus script URL, otherwise it is a different worker.
- Not removing closed ports from the
portslist. Ports of dead tabs pile up and the broadcast starts writing into nowhere. - Confusing SharedWorker with Service Worker. The first is a shared computation thread, the second is a network proxy with its own lifecycle.
- Relying on SharedWorker as the only way to sync tabs. Because support is uneven, keep a fallback such as
BroadcastChannel.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.