/ 5 min read / chromeextensionjavascriptchrome web store

Making Your First Chrome Extension

We all have our favorite web browser with our favorite extensions loaded, but have you ever considered writing your own? You've probably had at least one idea for something that it'd be nice to have, but there's nothing out there that quite does what you're looking for.

As technology becomes more and more a part of our everyday lives, it's useful to understand how to manipulate it to suit our individual needs. These days, knowing at least a little coding is like learning word processing or writing a basic html page was 15 years ago. It's becoming a basic computer skill.

In the past few months I've created a couple extensions to suit my own needs - unpolished but functional - and threw them in the Chrome Web Store just in case someone out there found them useful. Here's what I've learned.

(Before I start, you can find these examples on GitHub if you don't want to recreate them.)


Hello World!

Let's start with the easiest thing possible.

The most important file in a Chrome extension is the manifest.json file. It specifies everything about your extension, from meta information (name, author, version) to requested permissions, the locations of scripts, icons to display in the browser, and much more. You can read more about the many settings here, but you might want to wait until the end of this post, unless you just want to delve right into the deep end!

Here's a bare-bones manifest.json file for our very first "Hello World" extension. The first three lines are required, and the extension won't load without them - leave them be for now. The "background" field specifies a script to load, and the empty "browser_action" lets Chrome know there's no html page to display (pop up) when the extension icon is clicked (more on that later). Create a folder and copy this into a file named "manifest.json".

{
    "name": "My First Extension",
    "version": "1.0",
    "manifest_version": 2,
    
    "background": {
        "scripts": ["helloworld.js"]
    },
    "browser_action": {}
}

Create a second file and name it "helloworld.js", then paste the following into it. All we're doing is listening for the browserAction.onClicked event, and when it fires (when you click the icon for your extension) we can take some action.

chrome.browserAction.onClicked.addListener(function(tab) {
    alert('HELLOOOOO WORLD!!');
});

Now let's try it out...

Testing Your Extension Locally

Chrome makes it easy to test your extensions locally.

  1. Open the "extensions" pane where you'd normally look for new extensions, and then select the "Developer Mode" checkbox. New options appear, allowing you to load your extension and run it.
  1. Press the "Load unpacked extension..." button and navigate to the folder where you stored the two files you just created. Select the folder and press "Select".
  1. Look for the new generic icon (since we didn't specify an icon for it to use) in the upper-right corner, and press it. This fires our browserAction.onClicked event and executes our code - in this case a simple alert box.

Hello Popup!

Sometimes, having an extension do only one thing may be exactly what you want. Most of the time though, you'll probably want to display your own HTML page when a user clicks your extension icon. So let's try that next.

Open the manifest.json file, then take out the background script and give it a page to show instead.

{
    "name": "My Second Extension",
    "version": "1.0",
    "manifest_version": 2,

    "browser_action": {
        "default_popup": "popup.html"
    }
}

Create an html page named "popup.html" with some really advanced markup:

<h1>HELLO WORLD!!</h1>

On the extensions page, find your extension (it should be at the top of the list) and look for the "Reload" link. Click that, then click on the extension icon again.

chrome-dev-5

You should see the HTML page you just created. It's a thing of beauty, right? ;)


Popup 2 - Creating Emails

You can add any HTML you want in there. How about a box where someone can create an email? Change the HTML file to include a few relevant fields and a submit button:

<html>
    <head>
        <script src="script.js"></script>
    </head>
    <body style="width:200px">
        <p>Email Someone!</p>
        <p style="font-size:smaller"><em>(not as sophisticated as it sounds...)</em></p>
        <p>Email: <input id="emailAddress"></p>
        <p>Subject: <input id="emailSubject"></p>
        <p>Body: <textarea id="emailBody" style="min-height:50px;"></textarea></p>
        <p><input type="submit" id="send"></p>
    </body>
</html>

We can use Javascript to handle what happens when the submit button is pressed. Create the "script.js" file referenced above, and paste this code into it. (I registered the button's onclick event inside the page's load event to make sure that the page is fully loaded before trying to access elements on the page.)

window.addEventListener('load', function load(event) {
    document.getElementById('send').onclick = function() {
        var email = document.getElementById('emailAddress').value;
        var subject = document.getElementById('emailSubject').value;
        var body = encodeURIComponent(document.getElementById('emailBody').value);
        window.open(`mailto:${email}?subject=${subject}&body=${body}`);
    };
});

Popup 3 - Changing Colors

Let's try one more example. Say you'd like a series of buttons to do different things - you can do that too. Add a new section to the manifest.json file to specify a permission, so that the file looks like this:

{
    "name": "Button it up!",
    "version": "1.0",
    "manifest_version": 2,

    "browser_action": {
        "default_popup": "popup.html"
    },

    "permissions": [
        "activeTab"
    ]
}

Now we can make changes to the currently active tab, such as changing colors on it. The beauty of this particular permission is that it doesn't prompt the user for confirmation (unlike the "tabs" permission, which gives your extension the ability to affect any open tab).

Change the HTML file to throw a few buttons on the page:

<html>
    <head>
        <script src="script.js"></script>
    </head>
    <body style="width:200px">
        <p><input type="button" id="khaki" value="YELLOW"></p>
        <p><input type="button" id="lightblue" value="BLUE"></p>
        <p><input type="button" id="palegreen" value="GREEN"></p>
    </body>
</html>

And finally, change the script:

window.addEventListener('load', function load(event) {
    ['khaki','lightblue','palegreen'].forEach(function(color) {
        document.getElementById(color).onclick = function() {
            chrome.tabs.query({"active":true,"lastFocusedWindow": true}, function(tabs) {
                chrome.tabs.insertCSS(tabs[0].id, {'code':`html,body,div,p{background:${color}!important}`})
            });
        };
    });
});

This script is a little more complex than the previous one. I'm just taking a shortcut to register the onclick event for each of the three buttons. As long as the IDs on the buttons match the values in the array, it'll work. One more thing - the !important css tag is one you don't want to use very often, but in this case it ensures we can override pretty much any other styles on the page and change the background color to what we want.


Hello Options!

So far, I haven't shown an "options" page. Most likely you'll want someone to be able to edit and save some settings, and that's where an options page comes in.

For that, we'll need to make a couple tweaks to the manifest.json file, to specify an options page and to request permission to store data. (It really does control everything!)

{
    "name": "Options Please",
    "version": "1.0",
    "manifest_version": 2,

    "options_page": "options.html",

    "permissions": [
        "storage"
    ]
}

Create a new file - an "options.html" page that allows for some user input.

<script src="script.js"></script>
<div id="main" style="padding:30px">
    <p>Enter your name: <input id="name"></p>
    <input id="save" type="button" value="SAVE">
</div>

Next, modify the "script.js" file to store and retrieve the settings - in this case, just a name.

window.addEventListener('load', function load(event) {
    chrome.storage.local.get('name', function(result) {
        if (result != undefined && result.name != undefined) {
            document.getElementById('name').value = result.name;
        }
    });
    document.getElementById('save').onclick = function() {
        chrome.storage.local.set({'name': document.getElementById('name').value});
    };
});

Publishing an Extension

The last thing you might be interested in is actually publishing your extension in the Chrome Web Store once you've finished it. I won't repeat all the steps here, since there's already indepth documentation on how to publish in the Chrome Web Store.

The process wasn't too complicated, but like everything it's more clear after you run through it once. There's a nominal fee for uploading extensions - I think it's like $5 for up to 20 extensions. For most of us, that $5 is probably the only money we'll ever spend on it.


What else?

That's pretty much it for the basics, but the sky's the limit!

There are some truly amazing extensions, and if you check out the code, it sometimes numbers in the thousands or tens of thousands of lines of code. That reminds me, there's an awesome extension - Chrome Extension Source Viewer - that allows you to view the source code of any other extension in the store. It's super helpful if you're trying to figure out how someone did something, or just to verify that an extension isn't doing something malicious.

Learn more about extensions in the official docs. Again, all of the above examples are also available on GitHub.

If you're interested in seeing more of what I've learned, here's my first attempt as well as my more recent (and very tiny) attempt at writing extensions. Good luck!


Grant Winney

Grant Winney

I write when I've got something to share - a personal project, a solution to a difficult problem, or just an idea. We learn by doing and sharing. We've all got something to contribute.

Read More