DEV Community

Cover image for Using BroadcastChannel API with Vue to sync a ref across multiple tabs
Nico Prat for 365Talents

Posted on

Using BroadcastChannel API with Vue to sync a ref across multiple tabs

We try to save time for everyone in our team, not only developers: some people spend a lot of time configuring our app for clients or demos, so we make sure it's as smooth as possible. For instance, we try to make every change real time, so there's no need to reload the app for changes to appear.

Recently, we figured out those people usually work with multiple tabs open, making sure the configuration works as expected in multiple pages of the app. So we thought about syncing the configuration across tabs.

We didn't want to store it, neither in session, local storage or anything else, as we will then have to make sure it's always up to date.

That's when we came across the BroadcastChannel API, I didn't even know it existed. It's not fairly new, but Safari was the last to implement it according to CanIUse. Anyway it's largely supported now. You can think of it like the good old window.postMessage() from iframe, but across multiple tabs of the same origin.

Luckily for us, VueUse already made a little composable to ease its usage: https://vueuse.org/core/useBroadcastChannel/#usebroadcastchannel

const {
  isSupported,
  channel,
  post,
  close,
  error,
  isClosed,
} = useBroadcastChannel({ name: 'unique-name' })
Enter fullscreen mode Exit fullscreen mode

So we created a little in house composable based on it to make sure a ref is always synchronized among all tabs:

import { useBroadcastChannel, watchPausable } from '@vueuse/core';
import { nextTick, watch } from 'vue';

import type { Ref } from 'vue';

export const useSync = <T>(value: Ref<T>, name: string, options?: { immediate?: boolean; deep?: boolean }) => {
  // Name must be unique
  const { post, data } = useBroadcastChannel<T, T>({ name });

  // When value changes locally, update other tabs
  const { pause, resume } = watchPausable(
    () => value.value,
    (newValue) => {
      post(structuredClone(newValue));
    },
    options,
  );

  // When value changes in another tab, update it locally
  watch(
    () => data.value,
    async (newValue) => {
      // Prevent watch loop when updating config
      pause();
      value.value = newValue;
      await nextTick();
      resume();
    },
    options,
  );
};
Enter fullscreen mode Exit fullscreen mode

So now we can sync a ref with a single line:

const config = ref({})
useSync(config, 'config', { deep: true });
Enter fullscreen mode Exit fullscreen mode

And voilà! That's some hours saved each month across teams 🥳

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay