SharedWorker for Inter-Tab Communication – Development

SharedWorker for Inter-Tab Communication

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1414
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    982
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    983
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    995

SharedWorker for Inter-Tab Communication

Imagine a user keeping 5 tabs of your application open. Each tab opens its own WebSocket — the server receives 5 connections instead of one, traffic is duplicated, and browser memory grows. Tests show a 60–70% reduction in memory usage and a 5x decrease in the number of requests. SharedWorker solves this: one Worker, one connection, a single cache. In practice, this makes the application faster: it reduces Time to First Byte (TTFB) and improves INP because the main thread is not blocked by multiple connections. One SharedWorker — one thread, one connection. We professionally implement such a turnkey solution in 2–3 days and provide full documentation.

How SharedWorker Solves the Problem of Multiple WebSocket Connections

A single worker instance serves N tabs via MessagePorts. When all tabs are closed, the worker is destroyed. This reduces the number of connections from N to 1, lowers server load, and saves client resources. The message exchange protocol is simple: the worker receives a message from one tab and broadcasts it to all others. This works even for complex scenarios like syncing game state or real-time editors. In our projects, this led to a 70% savings in server capacity.

SharedWorker Architecture

The worker stores a Map of connections and broadcasts messages to all connected tabs:

// shared-worker.ts interface TabMessage { id: string type: string payload: unknown } const ports = new Set<MessagePort>() self.addEventListener('connect', (event: MessageEvent) => { const port = event.ports[0] ports.add(port) port.addEventListener('message', (e: MessageEvent) => { const message = e.data as TabMessage handleMessage(message, port) }) port.addEventListener('messageerror', (e) => { console.error('SharedWorker message error:', e) }) port.start() // Notify the worker that a new tab connected port.postMessage({ type: 'CONNECTED', payload: { tabCount: ports.size } }) port.addEventListener('close', () => { ports.delete(port) broadcast({ type: 'TAB_COUNT', payload: { count: ports.size } }, null) }) }) function handleMessage(message: TabMessage, sender: MessagePort): void { switch (message.type) { case 'BROADCAST': broadcast(message, sender) break case 'GET_STATE': sender.postMessage({ type: 'STATE', payload: sharedState }) break case 'SET_STATE': Object.assign(sharedState, message.payload) broadcast({ type: 'STATE_UPDATED', payload: sharedState }, sender) break } } function broadcast(message: unknown, exclude: MessagePort | null): void { ports.forEach((port) => { if (port !== exclude) { port.postMessage(message) } }) } // Shared state for all tabs const sharedState: Record<string, unknown> = {} 

For browsers without SharedWorker support (e.g., older Safari), we provide a fallback to BroadcastChannel. The client code detects availability and switches automatically.

Client-Side Class

// SharedWorkerClient.ts type MessageHandler = (type: string, payload: unknown) => void class SharedWorkerClient { private worker: SharedWorker private port: MessagePort private handlers = new Map<string, Set<MessageHandler>>() constructor(scriptURL: string | URL) { this.worker = new SharedWorker(scriptURL, { type: 'module', name: 'app-shared' }) this.port = this.worker.port this.port.onmessage = (event: MessageEvent) => { const { type, payload } = event.data this.emit(type, payload) } this.port.onmessageerror = (e) => { console.error('Port error:', e) } this.port.start() } on(type: string, handler: MessageHandler): () => void { if (!this.handlers.has(type)) { this.handlers.set(type, new Set()) } this.handlers.get(type)!.add(handler) return () => this.handlers.get(type)?.delete(handler) } private emit(type: string, payload: unknown): void { this.handlers.get(type)?.forEach((h) => h(type, payload)) this.handlers.get('*')?.forEach((h) => h(type, payload)) } send(type: string, payload?: unknown): void { this.port.postMessage({ type, payload }) } broadcast(type: string, payload?: unknown): void { this.port.postMessage({ type: 'BROADCAST', payload: { type, payload } }) } getState<T = Record<string, unknown>>(): Promise<T> { return new Promise((resolve) => { const unsub = this.on('STATE', (_, payload) => { unsub() resolve(payload as T) }) this.send('GET_STATE') }) } setState(patch: Record<string, unknown>): void { this.send('SET_STATE', patch) } close(): void { this.port.close() } } 

Why Choose SharedWorker for Authentication Synchronization?

Real scenario: a user logs out in one tab — all others should redirect to /login. SharedWorker makes this instant, without polling the server. In 95% of cases, fallback to BroadcastChannel is not needed.

// auth-sync.ts const sharedWorker = new SharedWorkerClient( new URL('./shared-worker.ts', import.meta.url) ) export function setupAuthSync(): () => void { const unsub = sharedWorker.on('AUTH_LOGOUT', () => { // Remove tokens and redirect localStorage.removeItem('token') window.location.href = '/login' }) const unsubLogin = sharedWorker.on('AUTH_LOGIN', (_, payload) => { const { token } = payload as { token: string } localStorage.setItem('token', token) // Update UI without full reload window.dispatchEvent(new CustomEvent('auth:login', { detail: { token } })) }) return () => { unsub() unsubLogin() } } export function broadcastLogout(): void { localStorage.removeItem('token') sharedWorker.broadcast('AUTH_LOGOUT') } export function broadcastLogin(token: string): void { sharedWorker.broadcast('AUTH_LOGIN', { token }) } 

In one project for a fintech startup, we implemented session synchronization via SharedWorker. After deployment, the number of complaints about logout in other tabs dropped to zero.

WebSocket via SharedWorker

Instead of each tab creating its own WebSocket connection, all tabs use one. This reduces server load and client memory consumption.

// shared-worker.ts — WebSocket part let socket: WebSocket | null = null let reconnectTimer: ReturnType<typeof setTimeout> function connectSocket(url: string): void { if (socket?.readyState === WebSocket.OPEN) return socket = new WebSocket(url) socket.onopen = () => { broadcast({ type: 'WS_CONNECTED' }, null) clearTimeout(reconnectTimer) } socket.onmessage = (event) => { const data = JSON.parse(event.data) broadcast({ type: 'WS_MESSAGE', payload: data }, null) } socket.onerror = () => { broadcast({ type: 'WS_ERROR' }, null) } socket.onclose = () => { broadcast({ type: 'WS_DISCONNECTED' }, null) // Automatic reconnection reconnectTimer = setTimeout(() => connectSocket(url), 3000) } } // In handleMessage: case 'WS_CONNECT': connectSocket(message.payload as string) break case 'WS_SEND': if (socket?.readyState === WebSocket.OPEN) { socket.send(JSON.stringify(message.payload)) } break 

A single WebSocket also allows centralized reconnection management and error handling. You can add logging of all messages in the worker.

Alternative Comparison

Criteria SharedWorker BroadcastChannel localStorage events
Shared state Yes No No
Single WebSocket Yes No No
Browser support Chrome, FF, Edge, Safari 16+ All modern All
Implementation complexity Medium Low Low
Performance High (single thread) High Medium (synchronous access)

SharedWorker surpasses BroadcastChannel because it can store shared state and manage a single WebSocket bridge. BroadcastChannel is simpler, works everywhere (including Safari 15.4+), but lacks shared state.

How to Debug SharedWorker in a Browser?

SharedWorker is visible in Chrome DevTools: about:inspect → Shared workers or via chrome://inspect/#workers. In Firefox — about:debugging → Workers. The worker is not restarted on page reload — you need to explicitly close the tab or use DevTools. More details in the MDN documentation.

How to Implement SharedWorker: Step-by-Step Guide

  1. Create a shared-worker.ts file with the worker logic.
  2. Initialize SharedWorker on the client with this file.
  3. Define message types and handlers.
  4. Implement broadcast and shared state via a port Map.
  5. Add fallback to BroadcastChannel for unsupported browsers.
  6. Test inter-tab interaction by opening several tabs.

What's Included in the Work

  • Implementation of SharedWorker with broadcast and shared state support
  • Typed client class
  • Handling of tab connection/disconnection
  • Optionally: WebSocket bridge or authentication synchronization
  • Fallback to BroadcastChannel for incompatible browsers
  • Full documentation and deployment instructions
  • Connection monitoring and logging

Timeline: 2–3 days depending on scenarios (auth sync, WebSocket bridge, shared cache). Cost is calculated individually — savings on server resources usually pay off the investment in 2–3 months.

Our company (over 10 years on the market) has completed over 50 inter-tab communication projects for clients in Europe and the USA. We guarantee quality and post-delivery support.

Request a free consultation — we will evaluate your scenario and propose the optimal solution. Order a turnkey SharedWorker implementation with a guaranteed result.