Building Cross-Platform Browser Extensions

Browser extensions can be super useful since they allow you to extend the browser 😏. I've been working on Klart.co — A bookmarking tool for designers, for about a year and have been building a few extensions the past year as well. Inevitably I've acquired some knowledge which I thought that I could share with you. I just released Klart's new extension so let's start with that.

This is what it looks like:

Klart's new extension

What can an extension do?

From a user standpoint, a browser extension can do pretty much anything the website you visit can, with the important exception that it has to ask for permission to do it. It wont ask every time, only when you install it, or if it wants more permissions. For example when a new version arrives.

One part (the content script) of the browser extension can be injected into your browser window on the website you're visiting. The other part (background script) can execute tasks in the background, and ask for permissions to post data to remote servers etc.

This means that a browser extension can read and manipulate websites that you visit, do something with the data and post it to a remote server. Now, that might sound scary, and it is. That's why you should think twice when you install a browser extension, and carefully read what it wants to do with you and your browser 🙀.

I can assure you we do no tracking at all at Klart. We have a business model based on you giving us money to make a great product, not ads, tracking and other bullshit.

What does Klart do?

Klart saves a screenshot, colors and a link to the website you're on. We call this a Snap. Creating a Snap is an awesome way to save design inspiration and it can also be used as enhanced bookmarking for saving bugs, research etc.

When you click the Klart extension we'll show you a little sidebar on the page you're on. You can fill out a note, add tags and select which board you want to save the Snap on. When you're done, we'll remove the sidebar and handle the actual saving in the background. This means that once you hit save, you can close that tab and go back to browsing 🎉.

The sidebar is in our content script. The fetching and saving is done in our background script. Keep reading to learn more about this.

Klart.co's extension

How do you structure an extension?

A common scenario is that an extension consists of one or more of these parts:

  • Popup
  • Content script
  • Background script

A popup is usually triggered by a click on the extension button. It's a html document that may have styles, javascript and whatever else a html document may have. They are very handy to use for most cases.

Content scripts are injected in the actual website you visit. They may read and manipulate that website. These can be useful if you want to inject sharing buttons on articles for your social media app or remove ads from a news site. Another case is to inject a user interface to save images when you over on them. You can even use them to inject a modal or sidebar to allow users to interact with your own app on that site. In general it offers a bit more flexibility and space for styling than a popup.

Background scripts are super useful to handle asynchronous operations. If you need to do a network request, such as an API call you should do it here. If the user closes her tab, the request will keep going.

Another important thing with background scripts is that you can make API requests which includes cookies. This means that if a user is logged in to our site Klart.co and make a POST request from our extension's background page, cookies are sent along with the requests, making it authenticated and valid.

Background scripts, popups and content scripts can communicate with messages 💌. Check out the example below.

What should you use?

To allow a user to interact, you usually need a popup or a content script. If you're doing network requests, you'll probably want a background script too. Content scripts are more powerful than popups and offer more flexibility. If you need to manipulate or read the website, you need a content script. Otherwise, you can go with a popup.

Psst! In content scripts it can be useful to reset all inherited CSS on an element. This can be done by the amazing CSS rule initial: all;.

How do you make one extension for all browsers?

Chrome and Firefox play along pretty well (with a few exceptions). With just some API changes, you can convert your Chrome extension to Firefox, and vice-versa. For instance, let's see how you send a message between a background scripts and a content script in Chrome vs Firefox:

// Chrome
// bg.js
chrome.runtime.onMessage.addListener((req, sender, res) => { 
  if (req.action === 'hello') return res('hello world');
  return true;
})

// content.js
chrome.runtime.sendMessage({ action: 'hello' }, res => {
  // -> hello world
  console.log(res);
})
// Firefox
// bg.js
browser.runtime.onMessage.addListener((req, sender, res) => { 
  if (req.action === 'hello') return res('hello world');
  return true;
});

// content.js
browser.runtime.sendMessage({ action: 'hello' })
  .then(res => {
    // -> hello world
    console.log(res);
  });

Notice how the only real difference here is that Firefox uses the browser object and Chrome uses the chrome object as well as Firefox using promises and Chrome callbacks. We can easily wrap this in our own API. What's left is just two different manifest files for each browser 🤗.

Safari is a different story though, it uses popovers (which are similar to popups), content scripts (same same) and a global page (similar to background scripts). Porting to Safari is not as straight forward. I'm currently working on porting Klart to Safari. I'm also working on a cross-platform API library called Hammock, which will handle Chrome, Firefox and Safari. Yes, I will open-source it when ready :).

What are the gotchas?

What I've seen so far is that Chrome is the most convenient browser to make extensions for. It takes just hours for approval and developer tools are decent enough. They do charge you a one-time fee of around $5 to submit extensions, but I can manage that 😬.

Firefox on the other hand has decent developer tools but it can take days, or even weeks to get a new version of you extension approved (this is also a good thing to keep bad stuff away). You also have to submit source code if you minimize or bundle your code (which you probably want to do). What's great about Firefox is that you don't have to pay any money to submit an extension.

Finally, Safari is the worst of the three. Mostly because they charge you the full developer fee to develop just a tiny extension 🤑🤑.

Should you make an extension?

Yes! Extending browsers is super powerful and we need more useful and funny extensions 😊.

What I've learnt

  • If you're thinking about supporting multiple browsers, think about it from the start and build your API to be cross-platform
  • It takes time to get Firefox extensions approved. If you have a server backend, make sure it handles legacy versions if you're running ahead with your Chrome extension.
  • If you have analytics on browser usage, use it! Don't make an extension nobody will use.
  • Do timely network requests in the background
  • Ask for permissions for what you need. Not more.
  • Don't be afraid to use a light framework. If your extension has complex view logic, you may use a framework. Preact is lighter than Jquery, remember that 🤓.
  • Don't install extensions you don't trust. If the product is free, you're the product.

That's it, let's talk more on Twitter @drikerf.