Comparison of data transfer methods between browser tabs in SPA context

Intspirit Ltd
5 min readSep 1, 2021

Intro

State management on the application level is the task for libraries such as Redux. However, there are situations when the application needs to synchronize a part of the state between instances of the application opened in different tabs of the same browser. As an example, it could be a user authorization state or a shopping cart in an online store. In this case, we need to be able to transfer the state between tabs of the same SPA.

Overview of data transmission methods and definition of criteria for their comparison

Generally, these methods can be divided into two groups: using a server or without it. Let’s briefly describe the pros and cons of the first group:

Pros:

  • We already have a protocol to work with the server (web-sockets etc)
  • It is possible to synchronize the state not only between tabs of the same browser but also between different devices.

Cons:

  • For each opened tab we need to have a connection with the server
  • Delays in data transfer

Methods without using a server are designed to mitigate the shortcomings above. In this article, we will focus on the second group and perform a shallow comparison of the following three methods:

1. Data transfer via Service worker

2. Data transfer using on change events of Localstorage

3. Data transfer via Broadcast Channel interface

Below, there is a list of criteria for comparison:

  • Browser support of the method.
  • How convenient is it to use this method?
  • How to get access to all tabs?
  • What format is used for data transfer?

To draw a better picture of the state which we’ll transfer between tabs, we take the change in the background color of the page:

The gif shows two tabs with an opened sample application that contains two buttons — “change state” and “unsubscribe”. When you click “change state” in any of the tabs, both tabs will change the background color, while when you click “unsubscribe”, the tab will stop receiving status updates from the other tabs.

Method 1 — Service worker

To create a communication channel through the Service worker, you need to create a file with the service worker code and register it in the code of your application:

Then, you need to subscribe to postMessage events in the code of the worker. And transfer data into all tabs as follows:

Next, we need to subscribe to postMessage events from the service worker in the component itself:

And as a final step — we need a function that will send a message to the service worker:

The complete code can be found in this branch:
https://github.com/EugenTepin/vanilla-state-share/tree/service-worker

WARNING! This code is provided solely as a working example and should not be used in production.

So, let’s try to evaluate this method according to the above criteria:

What about browser support?

This method is pretty well-supported https://caniuse.com/serviceworkers

How convenient is this method?

This method turned out to be the most difficult in terms of initialization. If you decide to use this method, you will have to think over the worker behavior at all stages of its life cycle. (more here).

In terms of gaining access to the SPA tabs, the service worker is great.
It uses the postMessage interface for data transfer — it allows you to transfer objects, which is convenient. It is also worth noting that the service worker can send a message to the same window/tab from which it came, allowing other components from the same tab to react to state changes.

Method 2 — Localstorage

To create a communication channel, we should subscribe to the storage event inside the component:

Then, to send the state to other tabs, we need to store it in localstorage:

The complete code of this component can be found in this branch: https://github.com/EugenTepin/vanilla-state-share/tree/localsrorage

What about browser support?

Supported in all major browsers.

How convenient is this method?

Event will occur in all tabs except the one that initialized this event, which might be a problem. If you want the other components on the page to receive these changes, a tab-level state manager such as Redux can help solve the problem.

WARNING! The storage event won’t work for session storage.
WARNING! Session storage might be unavailable in private (incognito) mode.

You’ll need to use a JSON string for data transfer, which is not very convenient. As with Service workers, the state is preserved for later user visits.

Method 3 — Broadcast channel

To create a communication channel, we must subscribe to the channel by name inside the component, and the API will independently determine whether to create a new channel or connect to an existing one.

Sending a message to other tabs is a trivial process:

We create an instance of BroadcastChannel & specify the name of the channel. We use postMessage to send the message.

Subscribing to the channel is also simple:

The complete code can be found in this branch: https://github.com/EugenTepin/vanilla-state-share/tree/broadcast-channel

What about browser support?

Not supported in IE & Safari:

https://developer.mozilla.org/en-S/docs/Web/API/Broadcast_Channel_API

https://caniuse.com/broadcastchannel

How convenient is this method?

This method is very easy to use. It is also worth noting that we can create several different channels if necessary, for this it is enough to pass a different name to the constructor. This allows some flexibility, and there are no restrictions regarding the structure of the message, since postMessage, which is used for transmission, allows to pass objects.
One flaw is that the state will not be saved if all application tabs are closed. As well as the fact that the message will not arrive in the window/tab from which it was sent.

WARNING! If this API seems convenient to you, but you are confused by the browser support, then perhaps take a look at this project https://github.com/pubkey/broadcast-channel

Conclusions

For a real project that needs support for as many browsers as possible, it’s good to choose Service Workers, because:

  • The technology is implemented in all modern browsers.
  • The state is passed to all tabs and/or windows (including the one from which it was sent)
  • Objects are passed instead of JSON strings.

Note: if you need broader browser support, then you should take a look at localstorage. In this case, however, you won’t be able to pass objects, only JSON strings (oops, bad magic!)

BroadcastChannel’s interface is extremely user-friendly & easy to use, but it’s not worth using in production in its pure form due to poor support in browsers.

Subscribe to our blog and stay healthy!

--

--