A Guide for Migrating to Chrome Extension Manifest v3

As we covered in a previous #TechTalk, Google is updating its manifest for Chrome extensions, and for good reason.  If you want to have a Chrome extension in 2023 and beyond, you’ll need to migrate to Manifest Version 3 (“Mv3”) eventually.

The official migration guide is here. But it turns out that the migration is complex, and has serious changes in store for the way Chrome extensions are built, how they behave, and what they’re permitted to do. At Evinced, we use several Chrome extensions and have already done the heavy lifting to move to Mv3.  We thought we’d share what we learned, in the hope of saving you some time, and maybe even heartache.  (It cost us more than a little of both!)

Start with the manifest

Every extension has a ‘manifest.json’ file that describes the permissions, files, components, and other configurations of the extension. Mv3 introduces a slightly different syntax of the manifest.json file, and it’s a good place to start the migration.

The nice thing about starting with the manifest is that Google Chrome helps you validate the syntax when you upload the extension to chrome under chrome://extensions.  For example, Chrome will return an error if there’s a misconfiguration in your file, like so:

Alt: Chrome’s error popup after loading a misconfigured manifest.json v3 file

Check the error console

Once the manifest is valid, we can run the extension on Chrome, and start to look for things that do not work in the new version.

A good place to look is the Errors window in chrome://extensions. To see it, visit chrome://extensions, load the extension and click “Errors.” Unlike manifest configuration errors, which are based on syntax, the errors here occur in run time, so they will add up over time.

Alt: Errors log from chrome://extensions page after clicking on the `Errors` button


With luck,  the error messages you see will be helpful, so keeping an eye on the Errors window while migrating is a good idea.  

Tips for importing external resources

Mv3 blocks any resource fetched from an external source, including Javascript, fonts, CSS files, and more, with the exception of XHR/fetch requests.  You may find that there’s a lot of work to do here since even many common Google features can be considered to be external requests. Here are some examples of changes we had to make.

Google Analytics in Mv3

The official way in Google’s documentation to import Google Analytics won’t work in Mv3:

Javascript reference for Google Analytics

That’s because Mv3 does not allow downloading and executing the js file used in the <script> tag.

To overcome this, we downloaded the JS file and all of the other files it uses in advance and bundled them into our source code.

In case you are using Google Analytics only, and don’t need Google Tag Manager, you can download the analytics.js file only.

Google Fonts in Mv3

The Google Fonts service provides a popular and easy way to use different fonts without bundling them into the source code. Or, at least it used to. With Mv3, we needed to download the fonts, and the CSS files with their declaration, into our code base.

Luckily, there’s a great service called Google Webfont Helper that downloads the fonts in the desired weights and fallback and creates a CSS file with all the declarations.

Now we just need to import that CSS file, include the fonts in the packed Chrome Extension zip uploaded to the Google store, and include this file in the ‘manifest.json’ file.

The background page is now a service worker

From our own experience and conversations with fellow developers going through the same process, this is by far the most significant change in Mv3.

Service workers are not persistent

One of the configurations available for Mv2 background pages was the persistency setting, which could enable a background page to live forever, in the same process.  This allowed the use of Javascript variables to store data and keep it in memory for later use.

By contrast, a service worker is not persistent and can be shut down and restarted at any moment, with no guarantee of the timing of the restart.

Developers, including us, have mixed feelings regarding this approach. In any event, the solution required that any critical piece of background data be saved and fetched from external storage, chrome.storage.local

Goodbye localstorage

Speaking of which, chrome.storage.local completely replaces LocalStorage APIs in Mv3, partly because the background page is now a service worker (and as such does not have access to LocalStorage).  

This can be an easy fix, depending on the way localstorage is used in your application.

There are 2 alternatives, ‘chrome.storage.local‘ and ‘IndexedDB‘.

In our case, we switched to ‘chrome.storage.local’ since we didn’t need all the capabilities of ‘IndexedDB.’

The main change here was the move from a synchronous API to an asynchronous one, adding many ‘async/await’ declarations to the code base.

No more access to the window object

Code that runs inside the background could use the ‘window’ object in Mv2. This is not allowed in Mv3 because of the move to a ServiceWorker from an HTML page. While you may not use the window object directly, many libraries do, and won’t work unless modified to work in a different way.

Here are some issues we encountered and their fixes.

Using Axios in the background

Axios is a Promise-based HTTP client for the browser and node.js. We use it to make HTTP calls to the server. Unfortunately, it uses the window object, so it won’t work inside a service worker.

As a workaround, we used an adapter by @vespaicach as described in this great post by Himanshu Patil. After configuring Axios to work with the adapter:

import fetchAdapter from  '@vespaiach/axios-fetch-adapter';
const instance = axios.create({
  adapter: fetchAdapter

});

It works!

Using Authentication libraries in the background

Some user authentication libraries depend on the window object as well and will fail to run in the background under Mv3.

In our case, we needed to change the authentication mechanism so it will work mainly from the content script, and data will be passed on a need-to-know basis to the background.

Conclusion

Chrome Extension’s Manifest v3 is around the corner, bringing much-needed security and permissions improvements in the Chrome Extensions world.

Making the migration could be easier, to be sure. But we think it’s worth it, in the hope that this shift will on balance give a push to do-good extensions, like those we are building for a more accessible world.