How SharedWorker Works Across Multiple Browser Tabs

The SharedWorker interface allows multiple browser contexts—such as tabs, windows, or iframes—to run and interact with a single shared JavaScript background thread. By binding connections to a centralized script instance via explicit communication ports, web applications can centralize network requests, synchronize application state, and manage shared resources without duplicating background processing across each open tab.

The Same-Origin Instance Model

When a browser tab instantiates a new SharedWorker(scriptURL, [name]), the browser checks if a worker instance already exists for that exact script URL and name under the same origin (protocol, domain, and port).

Because execution is scoped to the origin, data within the worker thread resides in a single, shared memory space accessible by any eligible browsing context.

Port-Based Communication

Unlike standard dedicated Web Workers that use the worker instance directly to send and receive messages, SharedWorker relies on MessagePort objects provided by the Channel Messaging API.

  1. Tab Connection: When a tab initializes the worker, it receives a reference to a dedicated port via worker.port.
  2. Worker Event Handling: Inside the shared worker script, the runtime fires the onconnect event each time a new tab connects.
  3. Port Assignment: The connect event contains a ports array containing the communication port corresponding to that specific tab (e.ports[0]).
  4. Data Transfer: Both the tab and the shared worker communicate asynchronously through their respective port objects using port.postMessage() and the port.onmessage event handler (or port.addEventListener('message', ...) combined with port.start()).
// main.js (Tabs)
const worker = new SharedWorker('worker.js');
worker.port.start();

worker.port.onmessage = (event) => {
  console.log('Message received from SharedWorker:', event.data);
};

worker.port.postMessage('Hello from Tab');
// worker.js (SharedWorker)
const connections = [];

self.onconnect = (event) => {
  const port = event.ports[0];
  connections.push(port);
  port.start();

  port.onmessage = (e) => {
    // Broadcast data to all connected tabs
    connections.forEach((conn) => {
      conn.postMessage(`Broadcasting: ${e.data}`);
    });
  };
};

Lifecycle and Resource Management

A SharedWorker remains active as long as at least one connected tab, window, or iframe maintains an open port to it. Once all connected browsing contexts are closed or explicitly close their ports, the browser terminates the worker instance to reclaim system memory.

This architecture is especially effective for maintaining a single persistent WebSocket or Server-Sent Events (SSE) connection, centralizing data caching, and eliminating duplicate network polling across multi-tab user sessions.