DEV Community

Cover image for Browser Extension - Extension architecture
Quentin Ménoret
Quentin Ménoret

Posted on


Browser Extension - Extension architecture

I just published a new extension on Chrome and Firefox that allows anyone to run Code Tours from the Github UI. More information about Code Tours and the extension in this blog post.

I thought it would be nice to write a series about how you could do exactly the same, step by step.

This first post will be about what an extension looks like in terms of architecture:
🛠 What are the main components? Let’s talk Background Script, Content Script and Injected Scripts
💡 Which one should you use for which purpose?
🤝 How to communicate between them?

The extension architecture

Background script

The heart of an extension is called a Background Script. This is simply a JavaScript script that runs in the browser as soon as the extension is loaded. It is not linked to any specific tab and can’t interact with their content. Its goal is to contain the state of your extension, and to implement all external calls or chrome API manipulation.

For instance, it’s from a Background Script that you would be able to focus a specific tab, or interact with the history or bookmarks of the browser. We won’t use such features for this extension, but we will need it to perform HTTP calls, and maintain the state of the application.

Content Script

A content script runs in the context of a single tab. The associated javascript code will get loaded when the tab opens, and stop as soon as it closes. You will be able to specify in which tabs your content scripts should be loaded (based on the URL for instance). We’ll see that in the next post.

A content script has access to most of the page context, including the DOM (you can add elements or alter the page from there). Note that because of security reasons, even though you have access to a window object, it won’t be the same instance as the one available to the code running in the website you are injecting to. This is meant to prevent you from accessing functions defined by another extension, or by the web page. If you need access to the window object, you would need to use injected scripts (the next part of this post!).

In order for the Content Script to communicate with a Background Script, you should use the Chrome Messaging API.

Sending a message is fairly easy and can be achieved with a single call:

chrome.runtime.sendMessage({message: "content"}, function(response) {
  console.log('message has been received: ', response);
Enter fullscreen mode Exit fullscreen mode

Injected Script

An injected script is just a bit of code you inject directly into the page, using DOM manipulation (for instance using a <script> div tag). In this context, the script will have access to the same window object as the website code, but won’t have access to the extension API.

For the injected script to communicate with a Content Script, you will use the postMessage API:

window.postMessage({ message: 'from the injected script' }, "*");
Enter fullscreen mode Exit fullscreen mode

A side note

None of these components are required to build a Browser extension. You could just have a content script, or a background script. But to have an injected script, you actually need a content script (since it is the one doing the injection).

There won’t be a need for an injected script in this tutorial, but you might need it if you were to expose a public API to the website (by creating methods on the window object for instance).

What it looks like

Schema of the architecture of an extension


This was a short introduction to how a browser extension works. Next time, we’ll see how to bundle it all together so you can test the extension! Feel free to follow me here if you want to check the next post when it's out:

Photo by Ricardo Gomez Angel on Unsplash

Top comments (5)

picwellwisher12pk profile image
Amir Hameed

What if you had to make extension with browser/popup page instead of content script?
I am having so much trouble making my extension using new standards. Chrome extension does not allow other protocols so my refreshes and HMR does not work.
Also new V3 manifest has many problems when it comes to Content Script Policy.

Any information about these?

gimsys profile image
George Farcas


qmenoret profile image
Quentin Ménoret

Thanks 😊

oscarrodar profile image
Oscar Rodriguez Arroyo

I've been doing extensions lately and this article gives a fair understanding of how these main components work. Thanks for sharing.

qmenoret profile image
Quentin Ménoret

Thank you! Happy to hear that 😊

Visualizing Promises and Async/Await 🤯

async await

Learn the ins and outs of Promises and Async/Await!