Build your first Chrome extension with chrome.tabs
Tags: javascript, codenewbie, webdev, tutorial
Published: 2020-04-02 01:40:04

---
title: Build your first Chrome extension with chrome.tabs
published: true
description: 
tags: #javascript, #codenewbie, #webdev, #tutorial
---

Browser extensions are a cool way to build on top of the basic parts of frontend web development: HTML, CSS, and JavaScript. In fact, they were my own on-ramp to frontend web development. What I really like about them is that all you need in order to get started building extensions is the stuff you make webpages with, and then by throwing in browser APIs, you can make really useful apps!

For this tutorial, we're going to make a Chrome extension that rearranges the tabs in your browser so they're in order by URL. That way, if you end up with a whole lot of tabs open from a bunch of different websites, you can use this extension to put all the tabs from the same site together in Chrome's top bar so they're easier to navigate.

This tutorial assumes understanding of the basics of JavaScript, and it's recommended to know about **callback functions** and **the `Array.prototype.sort` method**. It is geared toward people who are getting started with writing browser extensions.

## Add an extension manifest

The first thing every browser extension needs is a **manifest**. A manifest is a JSON file, and it's sort of like "the blueprint of the app", telling you things like what picture to use as your extension's icon, which code the extension runs, and which parts of Chrome the app needs access to, such as web history or bookmarks.

If you are following along in this tutorial, make a folder titled `tab-sorter`, put a folder under it called `app`, and inside the `app` folder, add the file `manifest.json` with this
code in it:

```json
{
  "manifest_version": 2,
  "name": "Tab Sorter",
  "version": "1.0.0",
  "description": "Rearrange your tabs by URL"
}
```

We now have a manifest giving us the name of our extension, its version, and a little description. In fact, with this, we actually technically already have an extension we can load into Chrome, so let's try that! First go to the URL `chrome://extensions`, which is where you can manage your extensions in Chrome. Then, flip the switch that says **developer mode**:

![The URL and top bar of chrome://extensions page, with a switch labeled "developer mode" in the top right](https://dev-to-uploads.s3.amazonaws.com/i/h4mdu3pso6hys6qbsdad.png)

Then, you will see a button in the top left that says **Load Unpacked**, which lets you load an extension from one of your folders. Click it and select the folder `tab-sorter/app`, and now in the top bar you should be able to see an icon with a T to the right of your URL bar. That's the extension you're making.

The extension doesn't do anything yet, except make a dropdown of options appear, so let's change that by giving it a **popup**, an HTML page that appears when you click on the extension icon. In the `app` folder add this HTML to a file titled `popup.html`:

```html
<html>
  <head></head>
  <body>
    <h1>We're running a Chrome extension!</h1>
  </body>
</html>
```

We have a page that can appear in a popup! But if we just reloaded the extension now, it wouldn't actually do anything with `popup.html`. In order to use that HTML file as our popup, we need to add popup.html to the manifest in a **browser action**, which is one way of specifying which code your extension will use.

In `manifest.json`, add the `browser_action` to the file and it should now look like this:

```diff
  {
    "manifest_version": 2,
    "name": "Tab Sorter",
    "version": "1.0.0",
    "description": "Rearrange your tabs by domain name",
  
+   "browser_action": {
+     "default_popup": "popup.html"
+   }
  }
```

The `browser_action.default_popup` field tells us that when we click on our app's icon, popup.html's content will appear in a little window under the browser's top bar. Now in `chrome://extensions`, click through the "load unpacked" flow again, or just click the swirly arrow icon in your extension's panel on the page to reload the extension. Then, we should see our popup!

![The top bar of the Chrome extension, with the icon to our extension clicked, displaying a popup saying "We're running a Chrome extension!"](https://dev-to-uploads.s3.amazonaws.com/i/1ik2gnudg8ha7ird0gn3.png)

Now we have a popup, and just like in a regular HTML webpage, we can run JavaScript in our popup to make the app do all the same things we can do in a regular HTML/CSS/JS web app.

But unlike in regular webpages, in extensions we can use **browser APIs** to be able to work with different parts of the browser, such as the user's tabs. We'll get started on that in the next section!

Before we do that, though, can't have an app without an icon! Our icon can be any picture that has the same height and width, and luckily at [this page](https://www.iconfinder.com/iconsets/google-material-design-3-0), Google made a free set of icons to use, under the permissive MIT license! Download the one that looks like a couple arrows going left and right in 128x128 size, save it to `tab-sorter/app/128.png`. Then, change the `browser_action` section of your manifest to:

```diff
  "browser_action": {
-    "default_popup": "popup.html"
+    "default_popup": "popup.html",
+    "default_icon": {
+      "128": "128.png"
+    }
  }
```

Now if you reload your extension one more time, you should see this:

![The top bar of the Chrome extension with our icon and our HTML popup. The extension's icon is now two arrows going back and forth](https://dev-to-uploads.s3.amazonaws.com/i/oguqpwx95ytbgyj6nt7g.png)

Our progress so far is in [Commit 1](https://github.com/andyhaskell/tab-sorter/commit/69fbb02ce1f6b86b91feb6359072c59c752f3c72)

Now let's get our extension to work with browser tabs!

## Explore the chrome.tabs API

In order to sort our tabs, we need an API that allows us to "talk to" the Google Chrome tabs. The actions we want to be able to do are:

* List all the tabs in the current browser window
* See what website each tab is on so we can sort the tabs by URL
* Move the tabs around in the browser's top bar so they're in alphabetical URL order

Google Chrome gives you a ton of APIs letting you work with different browser features, and you can see the whole list [here](https://developer.chrome.com/extensions/api_index)! The one we want is `chrome.tabs`, and you can find its documentation [here](https://developer.chrome.com/extensions/tabs)!

If you look through the table of contents for one of Chrome's APIs like `tabs`, you can see the different types of JavaScript objects the API works with, methods in the API you can use, and events you can make your Chrome extension listen for and respond to.

Let's take a look at the `Tab` type, which is the data type we'll be working with. A `Tab` object tells us information about a single tab in our browser. A few of those pieces of information are:

* `id`, a number uniquely identifying the tab
* `windowId`, which tells us which window the tab is in
* `highlighted`, a boolean that tells us whether or not a given tab is the one we're looking at
* and the main field we want, `url`, which tells us what URL the tab is on

Since we found the browser tab object and we know it has a `url` field, the pseudocode for our extension will look something like:

```javascript
let tabs = getTheTabs();
sortTheTabs(by Tab.url);
for (let i = 0; i < tabs.length; i++) {
  moveTabTo(that tabs index inside the sorted array);
}
```

So the pieces of pseudocode we need to turn into real code are now `getTheTabs`, `moveTabTo`, and a `by Tab.url` function for sorting tabs by their URL. Let's start by looking for a function for getting all the tabs in the browser window we're using.

## Listing the tabs

In the **Methods** section in the API's table of contents, there's a lot of methods, like getting an individual tab by its ID number, opening and closing tabs, navigating to a different URL, and even changing a tab's CSS using `insertCSS`.

The method we want, for getting a list of all the tabs in the current window, is `chrome.tabs.query`, and its function signature is:

```javascript
function query(queryInfo, callback)
```

The `queryInfo` parameter is a JavaScript object that gives us options for narrowing down which tabs we want to get. So to get only the tabs in the browser window the user is currently in (like if the user had more than one Chrome window open), our `queryInfo` object would look like this:

```javascript
{windowId: chrome.windows.WINDOW_ID_CURRENT}
```

Then we have the `callback` parameter. `tabs.query`, and a lot of other Google Chrome API methods, are **asynchronous**. In order to be sure we're not blocking the JavaScript runtime while we're getting data from thr browser, when we run `chrome.tabs.query` or similar methods, we have Chrome start getting the tabs we're asking for with `queryInfo`, then let JavaScript keep running more code. Then, when Chrome gives us our list of tabs, the callback function runs to let us work with the tabs.

So instead of our code looking like:

```javascript
let tabs = chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT});
// sort the tabs
// rearrange the tabs
```

it will look more like:

```javascript
chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, (tabs) => {
  // sort the tabs
  // rearrange the tabs
});
```

Let's give `chrome.tabs.query` a try by having our popup show a bulleted list all the tabs we're on! In the body of `popup.html`, add the script tag:

```javascript
<script type="text/javascript" src="popup.js"></script>
```

and then make a new file `popup.js` with this code:

```javascript
chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, (tabs) => {
  document.write(`<h3>The tabs you're on are:</h3>`);
  document.write('<ul>');
  for (let i = 0; i < tabs.length; i++) {
    document.write(`<li>${tabs[i].url}</li>`);
  }
  document.write('</ul>');
});
```

Go to `chrome://extensions`, reload the extension one more time, click the popup, and you will get:

![Our Chrome extension popup, with the text "the tabs you are on are:", and a bullet point saying "undefined" for each of the tabs I'm on, which are chrome://extensions, golang.org, and rust-lang.org](https://dev-to-uploads.s3.amazonaws.com/i/d2tf7u7b3ldo2hovbbxg.png)

Why are all our list items saying `undefined`? The reason why that is, is that we're not able to see what URL is on the tabs because our app doesn't request **permission** to use tab data. We need to set that permission in our extension's manifest.

Go to your `manifest.json`, and add the line `permissions: ["tabs"]`

```diff
  {
    "manifest_version": 2,
    "name": "Tab Sorter",
    "version": "1.0.0",
    "description": "Rearrange your tabs by domain name",
+   "permissions": ["tabs"],

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

On browser extension stores, users are able to see what permissions an extension needs access to. So before Chrome lets an extension get access to what URLs are in each tab, the `tabs` permission needs to be listed in the manifest. With that permission now in our manifest, reload your Chrome extension one more time, and you should see:

![Our Chrome extension popup, with the text "the tabs you are on are:". Now each bullet point says the URL to one of the tabs I have open](https://dev-to-uploads.s3.amazonaws.com/i/mnfuitxvfig8ize17o2f.png)

All right! We can now get all our tabs! Now our app's overall pseudocode has just two more blanks to fill in: sorting the tabs, and moving tabs to rearrange them. So now our pseudocode looks like:

```javascript
chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, (tabs) => {
  sortTheTabs(by Tab.url);
  for (let i = 0; i < tabs.length; i++) {
    moveTabTo(tabs[i], i);
  }
});
```

Our progress so far is in [Commit 2](https://github.com/andyhaskell/tab-sorter/commit/829c8651752f8c36e8d6bebbc4dc30c103a86152).

## Sorting the tabs

Now that we have our `Tab`s, the next thing to figure out is how to sort them so they go in order of their URLs.

As we saw in the last section, we have a `Tab.url` field we can use to see each tab's URL. So to sort our array of tabs in alphabetical URL order, we can use JavaScript's core [`Array.prototype.sort` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).

If you haven't used `Array.prototype.sort` before, it allows you to rearrange the items in an array using the order you want. For example, if you ran:

```javascript
let a = ['JavaScript', 'C++', 'Go'];
a.sort();
console.log(a);
```

Then the strings in the array would now be in the order `['C++', 'Go', 'JavaScript']`, sorted in alphabetical order.

Since we're sorting objects instead of strings or numbers, though, we'll need to also pass in a **comparison function** that takes in two tabs and tells us which one should go earlier in the array. Running it will look like this:

```javascript
tabs.sort(byAlphabeticalURLOrder);
```

According to the rules of writing `Array.prototype.sort`, when two items in the array are compared with a comparison function:

* The items in the array are moved so the first item is before the second item if the function returns a number **less than 0**.
* The items in the array are moved so the first item is after the second item if the function returns a number **greater than 0**.
* If the comparison function returns **exactly 0**, the two items are considered to have equal values and stay put where they are in the array.

So when two tabs are passed into our comparison function:

* If the first tab's URL comes before the second tab's URL alphabetically, then we return -1 so the first tab comes earlier in the array.
* If the second tab's URL comes before the first tab's URL alphabetically, then we return 1 so the second tab comes earlier in the array.
* If the two tabs have identical URLs, we return 0, and they stay in the same order they were already in.

So let's turn this into code. Add this function at the top of `popup.js`

```javascript
function byAlphabeticalURLOrder(tab1, tab2) {
  if (tab1.url < tab2.url) {
    return -1;
  } else if (tab1.url > tab2.url) {
    return 1;
  }
  return 0;
}
```

We've got our comparison function! Now let's try using it in our listing function we made in the last section:

```diff
  chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, (tabs) => {
+   tabs.sort(byAlphabeticalURLOrder);
+
    document.write(`<h3>The tabs you're on are:</h3>`);
    document.write('<ul>');
    for (let i = 0; i < tabs.length; i++) {
      document.write(`<li>${tabs[i].url}</li>`);
    }
    document.write('</ul>');
  });
```

Reload your extension in `chrome://extensions`, open the extension's popup again, and now it should look like this!

![Our browser extension displaying the bulleted list of URLs for the tabs we have open in alphabetical order, even though in the top bar, the tabs' URLs are not in order](https://dev-to-uploads.s3.amazonaws.com/i/6emy85kjs6co39w3fl6x.png)

Great, we have our sorted list of tabs! Now to make it so the tabs rearrange in the top bar of the browser, we need just one more Chrome function!

Our progress so far is in [Commit 3](https://github.com/andyhaskell/tab-sorter/commit/3c0bd4da4f402cab170bebda28453480bb428ee6)

## Moving the tabs

Looking back in the docs for the Chrome Tabs API, we can see that the API gives us exactly the function we want for moving the tabs in the top bar, `chrome.tabs.move`! And the function signature is:

```javascript
function move(tabIds, moveProperties, callback);
```

* For the `tabIds` parameter, each tab has an ID number, `Tab.id`, uniquely identifying it. So if we're moving the tab with the ID number 250, we would do `move(250, moveProperties, callback);`
* `moveProperties` is an object describing where to move the tab to. So we could move the tab with ID number 250 to be the leftmost tab in our browser window by calling `move(250, {index: 0}, callback);`. Notice that tab indices are zero-indexed, so the leftmost tab in the window has index 0.
* Finally, we reach the optional `callback`. Like with `query`, the `move` function is asynchronous, so if we want something to happen right after the tab is moved, we run that in the callback function.

Let's give move a try by moving just the tab that's first in alphabetical URL order, to be the leftmost tab in your window. Edit `popup.js` like this:

```diff
  chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, (tabs) => {
    tabs.sort(byAlphabeticalURLOrder);
+   chrome.tabs.move(tabs[0].id, {index: 0});
-
-   document.write(`<h3>The tabs you're on are:</h3>`);
-   document.write('<ul>');
-   for (let i = 0; i < tabs.length; i++) {
-     document.write(`<li>${tabs[i].url}</li>`);
-   }
-   document.write('</ul>');
  });
```

To try this out, reload your extension in `chrome://extensions`, and move that tab so it's the rightmost tab in the browser. Now, click your browser extension's icon and that tab should move so it's the first tab from the left.

Because we had already run `tabs.sort`, all the tabs in the `tabs` array are now in the order we want them in on the top bar of the browser; the first tab in the array should be the leftmost one in the top bar, the second tab in the array is supposed to be the second tab in the top bar, and so on!

So if we have all the tabs in the order we want them in the array, we can move all of them into alphabetical URL order with this loop:

```diff
  chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, (tabs) => {
    tabs.sort(byAlphabeticalURLOrder);
-   chrome.tabs.move(tabs[0].id, {index: 0});
+   for (let i = 0; i < tabs.length; i++) {
+     chrome.tabs.move(tabs[i].id, {index: i});
+   }
  });
```

Reload the extension just one more time in `chrome://extensions`, click its icon, and your tabs should all now be rearranged in alphabetical order by URL!

Though one thing you might notice is a little off is that if, say, twitter.com and www.google.com are in your top bar, Twitter will come first in the rearranged tabs, even though Google alphabetically comes before Twitter. This is because "www" comes after "Twitter" in the URL. So if we were making this at a real company, a possible next user experience step might be to tweak our comparison function to ignore `https://` and `www.`.

I'll leave that, and brainstorming other ideas on improving user experience, as a challenge to do; I encourage you to keep experimenting with this app and the `chrome.tabs` API, but for now, we've got a sweet MVP (minimum viable product)!

If this is your first time writing a browser extension, congratulations, and I hope you want to go build some more! I definitely recommend checking out the [API index](https://developer.chrome.com/extensions/api_index) for Google Chrome to see the many other kinds of APIs your browser extensions can work with! 🎊 

The final product for this tutorial is in [Commit 4](https://github.com/andyhaskell/tab-sorter/commit/4972fedf62ddf9de568a38575e37632757c51bae).