chrome.runtime.onInstalled.addListener manifest version 3 migration from version 2

I have a 2yr old chrome extension for Gmail which I am updating to version 3.
I transfer JSON data to/from my server using iframes and this works well using version 2 or 3.

The problem I have is with background.js (which works with version 2)

//background.js reloads gmail when extension installed or refreshed

chrome.tabs.query({url: "*"}, function(tab) {
  if ( typeof tab[0].url != 'undefined' && tab[0].url.indexOf('') == 0 ) {


In manifest V2 it was declared like this:

"background": {
"scripts": ["background.js"],
"persistent": false //true


In manifest V3 we now have to use a service worker:

"background": [{
"service_worker": "background.js"


There are 2 questions:

  1. I believe I need this or do I? so that when the extension is updated in the chrome store and then in the browser this causes Chrome – Gmail to reload and use the latest version. Please correct me if I’m wrong in this assumption. If I’m in developer mode (chrome://extensions) using the unpacked version using the refresh button will reload the extension and Gmail.
  2. I can’t figure out how a service worker would work as they aren’t persistent by nature and this "Listener" needs to be listening all the time.
    Does anyone have an example of a service worker/background.js migration from Version 2 to 3?

Further info: Migrating from background pages to service workers


23 thoughts on “chrome.runtime.onInstalled.addListener manifest version 3 migration from version 2”

  1. Sounds like you’re a victim of the poorly written documentation article: the author is a web developer who seems to think it’s necessary to emphasize that a service worker is something different and better, whereas it’s conceptually the same as an event page script (aka "non-persistent background script") that we were using for all these past years.

    Since you were already using the event page, you can simply keep doing what you always did: register the API event handlers like chrome.runtime.onInstalled or chrome.tabs.onUpdated so Chrome remembers the registrations internally. When the worker terminates after 30 seconds of inactivity, if an event that you subscribed to occurs, Chrome will start the worker, the entire script will run again then your listener will be called. To reiterate, this works exactly the same as an event page always did.

    P.S. Your code for service worker is incorrectly adding [ and ]. Here’s the correct code:

    "background": {
      "service_worker": "background.js"

Leave a Comment