Back
Chapter 10

Communicating with Messages

View Source Code

Messaging is a very important part of Chrome extension that can cause a lot of confusion

However it’s so much easier than it seems if you know what you’re doing

I will try my best to explain it to you in simple terms

You can think of it as the central nervous system that connects the various parts of your code the background script the popup or the content scripts

The basics

The basics are super simple

You can send a message from any component and you can listen to a message from any other component

Now a message can contain any valid JSON object such as strings numbers boolean arrays or objects that can be represented in a string

This is very useful so you can come up with ways how you can differentiate between the various messages

Sending messages

To get started I will create a helper method that sends a message when a note is updated

I head over to my utils folder and create a new file

Send note update message

In this file I will use a lot of redundant code

Sorry for that it’s only for demonstration purposes

// utils/sendNoteUpdateMessage.ts
import getPageKey from './getPageKey';
import { storage } from 'wxt/storage';

const sendNoteUpdateMessage = async (tabId: number) => {
  const tab = await browser.tabs.get(tabId);
  if (!tab.url) return;
  
  const pageKey = getPageKey(tab.url);
  const notesStorage = await storage.getItem('sync:notesStorage') || {};
  const note = notesStorage[pageKey];
  
  const message = {
    type: 'noteUpdated',
    pageKey,
    note
  };
  
  await browser.tabs.sendMessage(tabId, message);
};

export default sendNoteUpdateMessage;

So what I have now is the note data that belongs to the given tab ID and I can send a message by using the sendMessage method

Receiving messages

Now let’s listen for messages in the content script

// content/index.ts
export default defineContentScript({
  matches: ['https://*/*'],
  main() {
    const pageKey = getPageKey(window.location.href);
    
    const showRibbon = () => {
      if (document.getElementById('tabnotes-ribbon')) return;
      
      const ribbon = document.createElement('div');
      ribbon.id = 'tabnotes-ribbon';
      ribbon.textContent = 'Has Note';
      document.body.appendChild(ribbon);
    };
    
    const hideRibbon = () => {
      const ribbon = document.getElementById('tabnotes-ribbon');
      if (ribbon) {
        ribbon.remove();
      }
    };
    
    // Listen for messages
    browser.runtime.onMessage.addListener((message) => {
      // Ignore messages for other pages
      if (message.type !== 'noteUpdated' || message.pageKey !== pageKey) {
        return;
      }
      
      // Show or hide ribbon based on whether note exists
      if (message.note) {
        showRibbon();
      } else {
        hideRibbon();
      }
    });
  }
});

Perfect

Now when a note is updated the content script receives the message and shows or hides the ribbon accordingly

Calling from popup

When the user types in the popup we need to send the message

// popup/main.ts
noteText.addEventListener('keyup', async () => {
  const [tab] = await browser.tabs.query({ 
    active: true, 
    currentWindow: true 
  });
  
  if (!tab.id || !tab.url) return;
  
  // Save to storage...
  await storage.setItem('sync:notesStorage', notesStorage);
  
  // Update icon and send message
  await changeIcon(tab.id);
  await sendNoteUpdateMessage(tab.id);
});

Calling from background

We also want to send messages when tabs change

// background.ts
export default defineBackground(() => {
  browser.tabs.onActivated.addListener(async (activeInfo) => {
    await changeIcon(activeInfo.tabId);
    await sendNoteUpdateMessage(activeInfo.tabId);
  });
  
  browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    if (changeInfo.url && tab.active) {
      changeIcon(tabId);
      sendNoteUpdateMessage(tabId);
    }
  });
});

Perfect

Now the ribbon updates automatically when you switch tabs navigate to new pages or edit notes

Message flow

Here’s how messages flow through TabNotes

  1. User types in popup → Popup saves to storage
  2. Popup calls sendNoteUpdateMessage
  3. Background sends message to content script
  4. Content script shows or hides ribbon

It’s that simple

What’s next

We’ve connected all the parts of our extension with messaging

The ribbon now updates in real time when notes change

In the next chapter we’ll create custom pages for our extension like an options page or welcome page