How to create Web Browser extension (chrome/edge)

Taas Ekpaye
10 min readJan 17, 2024

--

Hello, I'm Taas, and today we'll talk about web browser extensions.

What is an extension?

Let’s say you have a car. You bought it two years later. At that time, there were no big car play screens provided by the constructor. Now, to feel comfortable when driving, you want to add new features. So you get from a supplier a new car play and new LED to add to your existing car. In this case, we can say car play and LED are extensions that are added to your car.
In our case, let’s say browser extensions help you have a great experience with your daily internet activities.
Extensions can be used to customize the browsing experience, access web services, or handle many tasks.
Now we have to talk about extensions. Let’s jump into how to create one.

How do I create extensions?

Due to the variety of web browsers, syntaxes differ from one environment to another. We got two types of browsers (that I know):

  • chromium based browsers (Chrome,Microsoft Edge, Brave, Opera)
  • non chromium based browsers (Safari, Mozilla FireFox)

As you can see, working on chromium based extensions will target more browsers, and I’m a huge fan of Microsoft Edge. So I’ll work on extensions for Chromium based browsers. This extension will change the font weight (900) and style (italic) of any website.

Let’s go

Create a folder named theme_manager.
Make sure to have the following files created inside:
For the icon.png, feel free to add yours.

Manisfest.json


{
"manifest_version": 2,
"name": "Theme manager",
"version": "1.0",
"description": "A simple extension that changes the background color of website",
"permissions": ["activeTab"],
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
}

This file contains the metadata of our extension. what is metadata 🤔?

popup.js

document.addEventListener('DOMContentLoaded', () => {
// handle event and other stuffs here
});

here we handle all action that will occur on the popup.html

popup.html

<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Theme Manager</title>
<link rel="stylesheet" href="popup.css">
</head>

<body>
<script src="popup.js"></script>
</body>

</html>

This is the User Interface that will help user interact with the extension

popup.css

body {
width: 300px;
height: 300px;
}

All CSS directives will be placed here to help style our UI

content.js

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// listening to any incoming message from the theme_manager ui
});

the content.js file is the script that will be loaded every time you open a web page. It’s a javascript script that will run in the context of a web page. We can access the DOM through this file and do our juju tricks 😈.

How can I see my extension?

Calm down. I will show you the steps on Microsoft Edge. It’s pretty much the same on other browsers. basically go to the settings → extensions → activate the developer mode. or type edge://extensions/ in your address bar.

See the developer mode on

Next ??

Select Load Unpacked, then select the folder theme_manager (the folder containing all the files we created earlier). You should see the extension in the list of your extensions. 😁

Now that we have seen what files do, let’s get into the trouble.
We’ll update the following files:

popup.js

document.addEventListener('DOMContentLoaded', () => {
/* `const toggleBtn = document.getElementById('toggle-btn');` is assigning the DOM element with the
id 'toggle-btn' to the variable `toggleBtn`. */
const toggleBtn = document.getElementById('toggle-btn');

/* The code `toggleBtn.onclick = () => {}` is assigning an event handler function to the `onclick`
event of the `toggleBtn` element. When the `toggleBtn` element is clicked, the function inside the
curly braces will be executed. However, since the function is empty in this case, nothing will
happen when the button is clicked. */
toggleBtn.onclick = () => {
/* The line `console.log('Toggle button clicked');` is logging the message "Toggle button
clicked" to the console. This is a way to output information or debug messages during the
execution of the code. */
console.log('Toggle button clicked');
}
});

popup.html

<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Theme Manager</title>
<link rel="stylesheet" href="popup.css">
</head>

<body>
<!-- Add the button to handle user action -->
<div>
<button id="toggle-btn"> Toggle theme </button>
</div>
<script src="popup.js"></script>
</body>

</html>

After that, in the browser, click on the extension icon, and you will get something like this:

Theme Manager Extension UI

When you click on the button, nothing happens. Yes, it's normal. but you should notice in the console an output

Output occurs when clicked on the button

Now that our UI is beta-working, let's send some actions to the website.

To communicate between the extension and the current website, we are going to use chrome.tabs to send messages to the current tab and chrome.runtime.onMessage to receive messages from the extension.

We are going to update the content.js and popup.js files

popup.js

document.addEventListener('DOMContentLoaded', () => {


/* `const toggleBtn = document.getElementById('toggle-btn');` is assigning the DOM element with the
id 'toggle-btn' to the variable `toggleBtn`. */
const toggleBtn = document.getElementById('toggle-btn');

/* The code `toggleBtn.onclick = () => {}` is assigning an event handler function to the `onclick`
event of the `toggleBtn` element. When the `toggleBtn` element is clicked, the function inside the
curly braces will be executed. However, since the function is empty in this case, nothing will
happen when the button is clicked. */
toggleBtn.onclick = () => {
/* The line `console.log('Toggle button clicked');` is logging the message "Toggle button
clicked" to the console. This is a way to output information or debug messages during the
execution of the code. */
console.log('Toggle button clicked');

// -- new --
/* The code `chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { ... })` is
using the Chrome extension API to query for the currently active tab in the current window. */
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {

/* The code `chrome.tabs.sendMessage(tabs[0].id, { message: 'theme_toggle_dark' }, function
(response) { console.log(response); window.close() })` is using the Chrome extension API to
send a message to the content script running in the currently active tab. */
chrome.tabs.sendMessage(tabs[0].id, { message: 'theme_toggle_dark' },
function (response) {
console.log(response); // response is data sent back from the website
window.close() // close the popup window after the message the action on the web site is done
})
});

}
});

content.js

/* The code snippet is adding an event listener to the `chrome.runtime.onMessage` event. This event is
triggered when a message is sent from another part of the extension or from a content script. */
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
/* The code snippet is checking if the `message` property of the `request` object is equal to the
string `'theme_toggle_dark'`. If it is, it logs the value of `request.message` to the console
and sends a response back to the sender with a success message. */
if (request.message === 'theme_toggle_dark') {
console.log(request.message);
/* The `sendResponse({ message: { "theme_toggle_dark": "success" } });` line of code is sending
a response back to the sender of the message. It creates an object with a `message`
property, which itself is an object with a key-value pair of `"theme_toggle_dark":
"success"`. This response indicates that the message was successfully received and
processed. */
sendResponse({ message: { "theme_toggle_dark": "success" } });
}
})

What is going on ? 😟

I told you about juju 😂, je te dis ça là c'est juju tu crois pas ? 😂

Let’s keep it simple. First of all, when we click on the toggle button, we use the browser API to send a precise message to the active tab. and we just take action based on the message received from our extension. Terminé!

Not ok?

Ok, let’s say you have to reach town but you can’t drive. So you ask one of your brothers to drive. Your brother knows how to drive but doesn’t know the way. What will you do?
Tell him what road he must take. yeasssh that is the same.

“Turn left,” “Turn right,” and “Take that avenue” are the instructions you’ll give him. That is exactly what we are doing here.

You’re the popup.js, but for one reason you cannot access directly the car (DOM), so you rely on your brother (content.js) to execute the directives (“theme_toggle_dark”) you will instruct him. why that? for security. Do you want to ruin your life by driving when sick? That is not a good idea.

So let's keep moving
in the content.js we are going to add action to modify

content.js

/* The code snippet is adding an event listener to the `chrome.runtime.onMessage` event. This event is
triggered when a message is sent from another part of the extension or from a content script. */
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
/* The code snippet is checking if the `message` property of the `request` object is equal to the
string `'theme_toggle_dark'`. If it is, it logs the value of `request.message` to the console
and sends a response back to the sender with a success message. */
if (request.message === 'theme_toggle_dark') {
console.log(request.message);
document.body.style="background-color: #222222 !important; font-weight: 900 !important;";

/* The `sendResponse({ message: { "theme_toggle_dark": "success" } });` line of code is sending
a response back to the sender of the message. It creates an object with a `message`
property, which itself is an object with a key-value pair of `"theme_toggle_dark":
"success"`. This response indicates that the message was successfully received and
processed. */
sendResponse({ message: { "theme_toggle_dark": "success" } });
}
if (request.message === 'italic_toggle') {
console.log(request.message);
document.body.style="background-color: #222222 !important; font-weight: 900 !important; font-style: italic !important;";

/* The `sendResponse({ message: { "theme_toggle_dark": "success" } });` line of code is sending
a response back to the sender of the message. It creates an object with a `message`
property, which itself is an object with a key-value pair of `"theme_toggle_dark":
"success"`. This response indicates that the message was successfully received and
processed. */
sendResponse({ message: { "theme_toggle_dark": "success" } });
}
})

popup.js

document.addEventListener('DOMContentLoaded', () => {


/* `const toggleBtn = document.getElementById('toggle-btn');` is assigning the DOM element with the
id 'toggle-btn' to the variable `toggleBtn`. */
const toggleBtn = document.getElementById('toggle-btn');
const italicBtn = document.getElementById('italic-btn');

/* The code `toggleBtn.onclick = () => {}` is assigning an event handler function to the `onclick`
event of the `toggleBtn` element. When the `toggleBtn` element is clicked, the function inside the
curly braces will be executed. However, since the function is empty in this case, nothing will
happen when the button is clicked. */
toggleBtn.onclick = () => {
/* The line `console.log('Toggle button clicked');` is logging the message "Toggle button
clicked" to the console. This is a way to output information or debug messages during the
execution of the code. */
console.log('Toggle button clicked');


/* The code `chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { ... })` is
using the Chrome extension API to query for the currently active tab in the current window. */
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {

/* The code `chrome.tabs.sendMessage(tabs[0].id, { message: 'theme_toggle_dark' }, function
(response) { console.log(response); window.close() })` is using the Chrome extension API to
send a message to the content script running in the currently active tab. */
chrome.tabs.sendMessage(tabs[0].id, { message: 'theme_toggle_dark' },
function (response) {
console.log(response); // response is data sent back from the website
window.close() // close the popup window after the message the action on the web site is done
})
});

}
italicBtn.onclick = () => {
/* The line `console.log('Toggle button clicked');` is logging the message "Toggle button
clicked" to the console. This is a way to output information or debug messages during the
execution of the code. */
console.log('Toggle button clicked');


/* The code `chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { ... })` is
using the Chrome extension API to query for the currently active tab in the current window. */
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {

/* The code `chrome.tabs.sendMessage(tabs[0].id, { message: 'theme_toggle_dark' }, function
(response) { console.log(response); window.close() })` is using the Chrome extension API to
send a message to the content script running in the currently active tab. */
chrome.tabs.sendMessage(tabs[0].id, { message: 'italic_toggle' },
function (response) {
console.log(response); // response is data sent back from the website
window.close() // close the popup window after the message the action on the web site is done
})
});

}
});

As you can see, it works. That is just the beginning. All you have to do is understand the concept.

There are more and more things you can do. That is just the fundamentals. More tweaks are possible, like translating a page. Check all links on a page. Transform a website site to suit some purpose. We all rely on you now. Go and do some spicy extensions.

Here are some links

The code source is available here
Microsoft documentation

Usefull extensions

--

--

Taas Ekpaye

Software Engineer from Togo. Curious, and enthousiast.