How to Build a Chrome Extension
I made a Chrome extension this weekend because I found I was doing the same task over and over and wanted to automate it. Plus, I’m a nerd living through a pandemic, so I spend my pent-up energy building things. I’ve made a few Chrome Extensions over the years, hope this post helps you get going, too. Let’s get started!
Create the manifest
The first step is creating a
manifest.json file in a project folder. This serves a similar purpose to a
package.json, it provides the Chrome Web Store with critical information about the project, including the name, version, the required permissions, and so forth. Here’s an example:
"manifest_version": 2, "name": "Sample Name", "version": "1.0.0", "description": "This is a sample description", "short_name": "Short Sample Name", "permissions": ["activeTab", "declarativeContent", "storage", "<all_urls>"], "content_scripts": [ "matches": ["<all_urls>"], "css": ["background.css"], "js": ["http://css-tricks.com/background.js"] ], "browser_action": "default_title": "Does a thing when you do a thing", "default_popup": "popup.html", "default_icon": "16": "icons/icon16.png", "32": "icons/icon32.png"
You might notice a few things- first: the names and descriptions can be anything you’d like.
The permissions depend on what the extension needs to do. We have
["activeTab", "declarativeContent", "storage", "<all_urls>"] in this example because this particular extension needs information about the active tab, needs to change the page content, needs to access
localStorage, and needs to be active on all sites. If it only needs it to be active on one site at a time, we can remove the last index of that array.
A list of all of the permissions and what they mean can be found in Chrome’s extension docs.
"content_scripts": [ "matches": ["<all_urls>"], "css": ["background.css"], "js": ["http://css-tricks.com/background.js"] ],
content_scripts section sets the sites where the extension should be active. If you want a single site, like Twitter for example, you would say
"browser_action": "default_title": "Does a thing when you do a thing", "default_popup": "popup.html", "default_icon": "16": "icons/icon16.png", "32": "icons/icon32.png"
There are things in
browser_action that are also optional. For example, if the extension doesn’t need a popup for its functionality, then both the
default_popup can be removed. In that case, all that’s needed the icon for the extension. If the extension only works on some sites, then Chrome will grey out the icon when it’s inactive.
chrome://extensions/from the browser’s address bar and enable developer mode. That activates the “Load unpacked” button to add the extension files. It’s also possible to toggle whether or not the developer version of the extension is active.
I would highly recommend starting a GitHub repository to version control the files at this point. It’s a good way to save the work.
The extension needs to be reloaded from this interface when it is updated. A little refresh icon will display on the screen. Also, if the extension has any errors during development, it will show an error button with a stack trace and more info here as well.
If the extension need to make use of a popup that comes off the extension icon, it’s thankfully fairly straightforward. After designating the name of the file with
browser_action in the manifest file, a page can be built with whatever HTML and CSS you’ll like to include, including images (I tend to use inline SVG).
In that file, start by creating functionality and we’ll have access to the popup DOM like this:
document.addEventListener("DOMContentLoaded", () => var button = document.getElementById("submit") button.addEventListener("click", (e) => console.log(e) ) )
If we create a button in the
popup.html file, assign it an ID called
submit, and then return a console log, you might notice that nothing is actually logged in the console. That’s because we’re in a different context, meaning we’ll need to right-click on the popup and open up a different set of DevTools.
We now have access to logging and debugging! Keep in mind, though, that if anything is set in
localStorage, then it will only exist in the extension’s DevTools
localStorage; not the user’s browser
localStorage. (This bit me the first time I tried it!)
Running scripts outside the extension
This is all fine and good, but say we want to run a script that has access to information on the current tab? Here’s a couple of ways we would do this. I would typically call a separate function from inside the
DOMContentLoaded event listener:
Example 1: Activate a file
function exampleFunction() chrome.tabs.executeScript(() => chrome.tabs.executeScript( file: "content.js" ) )
Example 2: Execute just a bit of code
This way is great if there’s only a small bit of code to run. However, it quickly gets tough to work with since it requires passing everything as a string or template literal.
function exampleFunction() chrome.tabs.executeScript( code: `console.log(‘hi there’)` )
Example 3: Activate a file and pass a parameter
Remember, the extension and tab are operating in different contexts. That makes passing parameters between them a not-so-trivial task. What we’ll do here is nest the first two examples to pass a bit of code into the second file. I will store everything I need in a single option, but we’ll have to stringify the object for that to work properly.
function exampleFunction(options) chrome.tabs.executeScript( code: "var options = " + JSON.stringify(options) , function() chrome.tabs.executeScript( file: "content.js" ) )
Even though the manifest file only defines two icons, we need two more to officially submit the extension to the Chrome Web Store: one that’s 128px square, and one that I call
icon128_proper.png, which is also 128px, but has a little padding inside it between the edge of the image and the icon.
Keep in mind that whatever icon is used needs to look good both in light mode and dark mode for the browser. I usually find my icons on the Noun Project.
Submitting to the Chrome Web Store
Now we get to head over to the Chrome Web Store developer console to submit the extension! Click the “New Item” button, the drag and drop the zipped project file into the uploader.
From there, Chrome will ask a few questions about the extension, request information about the permissions requested in the extension and why they’re needed. Fair warning: requesting
“tabs” permissions will require a longer review to make sure the code isn’t doing anything abusive.
That’s it! This should get you all set up and on your way to building a Chrome browser extension! 🎉