@sitnikov/tab-sync
v1.0.2
Published
Browser tab synchronization system using SharedWorkers for coordinating multiple tabs
Downloads
10
Readme
Tab Sync
A library for coordinating browser tabs using SharedWorkers. Tabs can discover each other, exchange messages, and stay synchronized across page reloads and navigation.
Installation
npm install @sitnikov/tab-syncQuick Start
In Your SharedWorker
// worker.js
import { TabSyncServer } from "@sitnikov/tab-sync";
const server = new TabSyncServer({
scope: self, // SharedWorkerGlobalScope
});
server.start();In Your Browser Tabs
// main.js
import { TabSyncClient } from "@sitnikov/tab-sync";
const client = new TabSyncClient({
sharedWorkerPath: "/worker.js",
});
// Listen for tab updates
client.onTabsChanged = (tabs) => {
console.log("Connected tabs:", tabs);
// Each tab has: { id, createdAt, dynamicInfo: { title, url } }
};
client.start();Configuration
TabSyncClient Options
const client = new TabSyncClient({
sharedWorkerPath: "/worker.js", // Required: Path to SharedWorker script
sharedWorkerOptions: {
// Optional: SharedWorker options
name: "My Tab Sync",
type: "module",
},
});TabSyncServer Options
const server = new TabSyncServer({
scope: self, // Required: SharedWorkerGlobalScope
getExtraPingData: () => ({
// Optional: Extra data to broadcast
serverStatus: "connected",
timestamp: Date.now(),
}),
});API Reference
TabSyncClient
Properties
tabs: TabInfo[]- Array of all connected tabs (including current)myTabInfo: TabInfo- Information about the current tab
Methods
start(): void- Connect to SharedWorker and begin synchronizationsendMessageToServer<PayloadT, ResponseT>(messageType: string, payload: PayloadT, timeout?: number): AbortablePromise<ResponseT>- Send custom message to SharedWorker serveronCustomMessage<PayloadT, ResponseT>(messageType: string, handler: MessageHandler<undefined, PayloadT, ResponseT>): void- Register handler for custom messages from server
Events
onTabsChanged?: (tabs: TabInfo[]) => void- Called when tabs changeonExtraPingDataChanged?: (data: T) => void- Called when extra data changes
TabSyncServer
Properties
activeTabs: TabInfo[]- Array of currently active tabs (within 5 second timeout)
Methods
start(): void- Begin listening for tab connectionspingAllTabs(): void- Manually ping all connected tabssendMessageToTab<PayloadT, ResponseT>(tabId: number, messageType: string, payload: PayloadT, timeout?: number): AbortablePromise<ResponseT>- Send custom message to specific tabonCustomMessage<PayloadT, ResponseT>(messageType: string, handler: MessageHandler<PartialTabInfo, PayloadT, ResponseT>): void- Register handler for custom messages from tabs
Types
interface TabInfo {
id: number; // Unique tab identifier
createdAt: number; // Tab creation timestamp
dynamicInfo: {
title: string; // Current page title
url: string; // Current page URL
};
}Usage Patterns
Basic Tab Awareness
// Display connected tabs
client.onTabsChanged = (tabs) => {
const tabList = tabs.map((tab) => `Tab ${tab.id} (${tab.dynamicInfo.hidden ? "hidden" : "visible"}): ${tab.dynamicInfo.title}`).join("\n");
document.getElementById("tabs").textContent = tabList;
};Cross-Tab Communication with Extra Data
// SharedWorker with extra data
const server = new TabSyncServer({
scope: self,
getExtraPingData: () => ({
connectionStatus: wsConnected ? "online" : "offline",
messageCount: messageQueue.length,
}),
});
// Browser tab receiving extra data
client.onExtraPingDataChanged = (data) => {
document.getElementById("status").textContent = data.connectionStatus;
document.getElementById("messages").textContent = data.messageCount;
};Custom Messaging Between Tabs
Tabs can send typed messages to the SharedWorker and receive responses asynchronously. This enables request-response patterns where tabs ask the SharedWorker for data or request actions to be performed.
// SharedWorker handles requests from tabs
server.onCustomMessage("getUserPrefs", async (userId, senderTab) => {
const prefs = await loadUserPreferences(userId);
return prefs;
});
// Tab sends message to SharedWorker and waits for response
const userPrefs = await client.sendMessageToServer("getUserPrefs", currentUserId);The SharedWorker can also send messages to specific tabs and wait for their responses. This is useful for commanding tabs to perform UI actions or asking them about their current state.
// SharedWorker asks a specific tab to show a notification
const result = await server.sendMessageToTab(tabId, "showNotification", {
title: "Hello",
message: "This is from another tab",
});
// Tab handles the notification request
client.onCustomMessage("showNotification", async (notification) => {
new Notification(notification.title, { body: notification.message });
return { shown: true, timestamp: Date.now() };
});Integration with Other Systems
// Coordinate with WebSocket connections
const server = new TabSyncServer({
scope: self,
getExtraPingData: () => ({
wsConnected: webSocket?.readyState === WebSocket.OPEN,
}),
});
// Update tabs when WebSocket status changes
webSocket.addEventListener("open", () => server.pingAllTabs());
webSocket.addEventListener("close", () => server.pingAllTabs());Features
- Automatic Discovery: Tabs automatically detect each other when they connect
- Real-time Updates: Title and URL changes are broadcast immediately
- Activity Detection: Inactive tabs (no response for 5+ seconds) are filtered out
- Generic Data Support: Broadcast custom data from SharedWorker to all tabs
- Custom Messaging: Send typed messages between tabs and SharedWorker with async responses
- Clean Lifecycle: Proper cleanup when tabs close or refresh
Browser Support
- SharedWorker: Requires browsers with SharedWorker support (Chrome, Firefox, Edge)
- Modern Browsers: Optimized for ES2018+ environments
- Cross-Origin: Handles same-origin requirements for SharedWorker
Further Reading
- Implementation Details - Internal architecture and ping/pong protocol
- SharedWorker Documentation
