The need for cross-tab communication is common in scenarios where there is a lot of content to browse or manipulate (e.g. blogs or CMS systems) and data consistency needs to be maintained. Most of these scenarios can be solved by bringing in a back-end service to keep the data consistent across multiple tabs by polling and so on. This article describes a pure front-end solution for cross-tab communication without introducing back-end services
Cookies & IndexedDB
To achieve consistency in data, it is easy to think of a unified data store. When there is a backend on board, the backend is really a unified data store for the frontend. In a pure front-end environment, the more common data stores include
sessionStorage cannot be used for cross-tab communication as it only lives in a single tab session. localStorage has other applications, which will be analysed in depth below. So in this section we will focus on cookies and IndexedDB.
The implementation is really quite simple - poll the data source to ensure that the data in each tab is up to date.
It is important to note that cookies are read and written synchronously and may block the main thread when the cookie is large causing the page to lag. The IndexedDB API is asynchronous, so there are no performance issues with cookies being read or written synchronously.
The idea of using a unified data source for cross-tab communication is relatively simple, but polling is inefficient when data changes are infrequent (essentially a consumer pull). Therefore, it would be better to use events to drive the response from the consumer (in the form of a producer push).
LocalStorage is also essentially a data source, but unlike the two data sources mentioned above, when the contents of localStorage change, a corresponding event is raised and all tabs under the same source can receive the event.
Of course, there are some disadvantages to this approach, which need to be noted when using
- Similar to cookies, localStorage reads and writes are synchronous, so when the amount of storage data is large, reading and writing may itself block the main thread and cause the page to lag.
- The value of localStorage only supports string, which is a bit troublesome when passing complex data.
- The page that triggers setItem will not receive the storage event (FYI: https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent).
Broadcast Channel is a web API that enables communication across tabs, windows, frames, iframes and even workers from the same source, and unlike localStorage, the message object sent can be of any Object type, making it ideal for cross-tab communication.
The downside of Broadcast Channel is its browser compatibility. As of this writing, Safari does not support Broadcast Channel (FYI https://caniuse.com/?search=broadcast).
The more common scenario for ServiceWorker is probably caching data and loading data to improve page loading speed. However, ServiceWorker itself also supports sending messages and can therefore be used to communicate across tabs.
The downside of using ServiceWorker for cross-tab communication is that you need to learn and understand ServiceWorker, and if your site doesn’t use ServiceWorker in the first place, using it just for cross-tab communication might be putting the cart before the horse.
If you want to communicate across tabs from different sources, you will have to use window.postMessage.
- Cross-tab communication is simple with cookies or IndexedDB, but polling performance needs to be considered
- LocalStorage Event can be used to transfer across tabs, but performance and some boundary scenarios need to be considered.
- If there are no browser compatibility requirements, Broadcast Channel is probably the best choice.
- ServiceWorker can also communicate across tabs, but you need to evaluate the cost of introducing ServiceWorker in advance
- If you need to communicate across tabs across domains, you can only choose window.postMessage