Building Chrome Extensions for Beginners -- Part 1: Theory
Tags: chrome, extensions
Published: 2022-12-21 00:14:09

by [Eni Onyedikachi](https://blog.openreplay.com/authors/eni-onyedikachi)

Welcome to **Chrome Extensions for Beginners!** The primary goal of this series is to introduce the basic concepts of Chrome Extensions and how to build one. At the end of this series, you will have a Pomodoro timer extension built with Javascript. See a [demo in YouTube](https://www.youtube.com/embed/0cjijlr1_CQ).

To build any Chrome extension, we must rely on the [Chrome developers' documentation](https://developer.chrome.com/). This is the single source of truth for anything extension related, whether it's  Chrome APIs or publishing on the  Chrome website. Chrome APIs are special Javascript functions or special *manifest file*(more on this later) fields that allow us to interact with the Chrome browser through our Chrome extension. They are denoted by the `chrome` keyword followed by the API name `chrome.[API name]`.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/JiekbQR.png)



## Manifest file
The manifest file is the core of every extension. This is how you tell the web browser what your Chrome extension is supposed to be doing and what Javascript, Html, and Css files the extension is composed of.

As per Chrome developers [documentation](https://developer.chrome.com/docs/extensions/mv3/getstarted/extensions-101/#extension-files):

> The extension's manifest is the only required file that **must** have a specific file name: `manifest.json`. It also has to be located in the extension's root directory. The manifest records important metadata, defines resources, declares permissions, and identifies which files to run in the background and on the page.

This means that the manifest file is a [JSON](https://www.json.org/json-en.html)(Javascript object notation)-formatted file.

Let's look at some code examples:

From the code you downloaded from the GitHub repo for this article, open the `chrome extension basics` folder in your code editor. It should look like this.
```
📦 Chrome-Extension-Series
 ┣ 🎨 icon.png
 ```

Create a new `manifest.json` file

``` 
 📦 chrome extension basics
 ┣ 🎨 icon.png
 ┣ 📄 **manifest.json**
```

After adding the new `manifest.json` file, add the following code inside it.
```javascript
{
    "manifest_version": 3,
    "name": "First Extension",
    "version": "1.0.0",
    "description": "My first extension",
    "icons": {
    "16": "icon.png",
    "48": "icon.png",
    "128": "icon.png"
  }
}
```
 
The preceding is a very basic definition of a manifest file. The `manifest_version` tells the browser which version (3 is the latest) to use; how you define your manifest file depends on the manifest version. It's best practice to use the latest version because older versions are deprecated. The `name` field is just the name of our extension. The `version` field represents the version of your Chrome extension. The `description` field is just the description of your Chrome extension. The `icons` field is an object that contains different sizes the extension icon can be scaled to when needed.

With the manifest file ready, the Chrome extension can be loaded on the browser.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/IWKZuYK.gif)
 
1. Open the Chrome browser
2. Click on the hamburger icon far right(as seen in the above gif)
3. Click on `more tools` > `extensions` 
4. Then toggle `developer mode` on
5. Click on `load unpacked`
6. Finally, select the `chrome extension basics` folder that contains the `manifest.json` file

After following the above steps, your Chrome extension should be loaded in the browser. If we look at the extensions page in Chrome, we will see our loaded extension.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/uDoBIG0.png)

Learn more about the manifest file [here](https://developer.chrome.com/docs/extensions/mv3/manifest/)

 

## Popup and Browser Actions

Now that we have loaded our basic extension let's add some interactivity. The *popup* is a common interactive element every extension has; it is what appears on the toolbar when you click on an extension's icon.

According to the docs:

> Use chrome.action API to control the extension's icon in the Google Chrome toolbar.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/j5v4Xc5.gif)

In the above demo, you can see that our extension icon(pin it) is greyed out and when it is clicked on nothing shows up, that is because the popup page has not been created. To create the popup page, we must implement the *[action setting](https://developer.chrome.com/docs/extensions/reference/action/)* in the manifest file.


```javascript
  {
  "manifest_version": 3,
  "name": "First Extension",
  "version": "1.0.0",
  "description": "My first extension",
  "icons": {
    "16": "icon.png",
    "48": "icon.png",
    "128": "icon.png"
  },
    + "action": {
    +    "default_icon": {
    +     "16": "icon.png",
    +        "24": "icon.png",
    +        "32": "icon.png"
    +      },
    +      "default_title": "My Extension Action Title",
    +  "default_popup": "popup.html"
    +  }

}
```

The value passed to the `default_popup` field is the HTML file that will be loaded when you click on the extension. Now create a new file called `popup.html`.

```
 📦 chrome extension basics
 ┣ 🎨 icon.png
 ┣ 📄 manifest.json
 ┣ 📄 **popup.html** 
 ```

 ```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>First Extension</title>
</head>
<body>
    <h1>My First Extension</h1>
</body>
</html>
```

We can now view our popup page in the browser.
***N/B: You must always refresh the extension anytime you change the manifest file, as seen in the demo below.***

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/OqICWjk.gif)

Now that we have our Html file loaded on our popup page, we can make it interactive with Javascript and style it with css. Create a `popup.css` file and connect `popup.html` to it.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>First Extension</title>
+   <link rel="stylesheet" href="popup.css">
......
</html>
```

```
📦 chrome extension basics
 ┣ 🎨 icon.png
 ┣ 📄 manifest.json
 ┣ 📄 popup.html
 ┣ 📄 **popup.css**
```
 
```css
body {
    width: 400px;
    height: 400px;
}

h1 {
    color: blue;
}
```

After styling `popup.html` with the preceding code, the popup text is now blue.

Now let's use Javascript to make the popup more interactive by showing the current time.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>First Extension</title>
    <link rel="stylesheet" href="popup.css">
</head>
<body>
    <h1>My First Extension</h1>
    <h2 id="time"></h2>
</body>
   <script src="popup.js"></script>
</html>
```


Now create the `popup.js` file.

```
📦 chrome extension basics
 ┣ 🎨 icon.png
 ┣ 📄 manifest.json
 ┣ 📄 popup.html
 ┣ 📄 popup.css
 ┣ 📄 **popup.js**
```
 
```javascript
const timeElement = document.getElementById('time')
const currentTime = new Date().toLocaleTimeString()
timeElement.textContent = `The time is: ${currentTime}`
```

Now when you click on the popup, the extension shows the current time.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/h9lbzDa.gif)

Let's make our extension more interactive using one of  `chrome.action` API methods. Let's set a badge on the popup that displays the text `TIME`. We will use the [`setBadgeText`](https://developer.chrome.com/docs/extensions/reference/action/#method-setBadgeText) method.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/v2FQWYe.png)

In `popup.js`, add the following code.

```javascript
const timeElement = document.getElementById('time')
const currentTime = new Date().toLocaleTimeString()
timeElement.textContent = `The time is: ${currentTime}`


chrome.action.setBadgeText({
   text: "TIME",
}, () => {
   console.log('Finished setting badge text')
})
```

Our extension now has a badge that shows the text `TIME,` and the message is also displayed on the console. So it works.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/Dcidug9.png)

 

## Options page

The options page is another way users interact with Chrome extensions, so let's create one for our extension. If you right-click on the extension icon, it shows `options`, but it is disabled because the options page has not been created yet.

To create an options page, we must add the `options` field in our `manifest.json`.

```javascript
{
  "manifest_version": 3,
  "name": "First Extension",
  "version": "1.0.0",
  "description": "My first extension",
  "icons": {
    "16": "icon.png",
    "48": "icon.png",
    "128": "icon.png"
  },
  "action": {
    "default_icon": {
        "16": "icon.png",
        "24": "icon.png",
        "32": "icon.png"
      },
      "default_title": "My Extension Action Title",
      "default_popup": "popup.html"
  },
+  "options_page": "options.html"
}

```

Now create an `options.html` file, which will be our extension options page file.

```
📦 chrome extension basics
 ┣ 🎨 icon.png
 ┣ 📄 manifest.json
 ┣ 📄 popup.html
 ┣ 📄 popup.css
 ┣ 📄 popup.js
 ┣ 📄 options.html
```

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Extension Options</title>
</head>
<body>
    <h1>My Extension Options</h1>
</body>
</html>
```

We can now view our extension's options page by right-clicking on the icon and selecting the `options` option.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/CwFsoYZ.gif)


Let's style and add interactivity to our options page.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="options.css">
    <title>My Extension Options</title>
</head>
<body>
    <h1>My Extension Options</h1>
    <input id="name-input" type="text" placeholder="Enter your name!" />
    <button id="save-btn">Save Options</button>
</body>
+ <script src="options.js"></script>
</html>
```

Our project directory will have the following structure:

```
📦 chrome extension basics
 ┣ 🎨 icon.png
 ┣ 📄 manifest.json
 ┣ 📄 popup.html
 ┣ 📄 popup.css
 ┣ 📄 popup.js
 ┣ 📄 options.html
 ┣ 📄 options.css
 ┣ 📄 options.js
``` 

 In `options.css`, add the following code.
 
```css
h1 {
    color: green;
}
```

In `options.js`, add the following code.

```javascript
const nameInput = document.getElementById("name-input")
const saveBtn = document.getElementById("save-btn")

saveBtn.addEventListener("click", () => {
    console.log(nameInput.value)
})
```

The code in the `options.js` gets the text in the input field and logs in to the console when the `save Options` button is clicked.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/VX4HD35.gif)

 



<h2>Open Source Session Replay</h2> 

_<a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a> is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![OpenReplay](https://blog.openreplay.com/banner-blog_1oYPGT.png)

_Start enjoying your debugging experience - <a href="https://github.com/openreplay/openreplay" >start using OpenReplay for free</a>._





## Chrome Storage API

We can interact with the data the user inputs into the options page, but we currently don't have a way to store any data on the options page. We need a storage system to store data on the options page and also make the data accessible on the popup page. To do all these, we use the [chrome storage API](https://developer.chrome.com/docs/extensions/reference/storage/).

As per the documentation:
> Use the `chrome.storage` API to store, retrieve, and track changes to user data.

There are some caveats to the storage API, but you can assume it works like any basic local storage API that works with Javascript.

Two things to know before you use the storage API:
1. You need permission from the user to use the storage API(which is added in the manifest file).
2. There are two main storage APIs
    * `chrome.storage.sync` API
    * `chrome.storage.local` API
The main difference between both is the `chrome.storage.sync` API will sync all the Chrome storage data across different browser sessions, whereas `chrome.storage.local` is local to a single session. However, it is good practice to work with `chrome.storage.sync` whenever you work with user options since you want the data to be saved across different browser instances.


Before we use storage API, it must be added in the `permissions` field in our `manifest.json` file.

```javascript
{
  "manifest_version": 3,
  "name": "First Extension",
  "version": "1.0.0",
  "description": "My first extension",
  "icons": {
    "16": "icon.png",
    "48": "icon.png",
    "128": "icon.png"
  },
  "action": {
    "default_icon": {
        "16": "icon.png",
        "24": "icon.png",
        "32": "icon.png"
      },
      "default_title": "My Extension Action Title",
      "default_popup": "popup.html"
  },
  "options_page": "options.html",
+  "permissions": ["storage"]
}
```

Then add the following code in `options.js.`

```javascript
const nameInput = document.getElementById("name-input")
const saveBtn = document.getElementById("save-btn")

saveBtn.addEventListener("click", () => {
    const name = nameInput.value;
    chrome.storage.sync.set({
        name,
    }, () => {
        console.log(`Name is set to ${name}`)
    })
})
```

In the preceding code, we use the Chrome storage API to store user input through the `set` method, which accepts the value you want to store and a callback.

Now, if you refresh the extension, go to the options page and check the console, the stored value is being logged.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/WcPUTkq.gif)

If we refresh the page, you will notice that even though we set the input value in storage, we haven't updated the inside of the `name` input to be the saved value.

To do this, we will use the `get` method to retrieve the value(`set` is for storing values, and `get` is for retrieving values). 

```javascript
const nameInput = document.getElementById('name-input')
const saveBtn = document.getElementById('save-btn')
.....

chrome.storage.sync.get(['name'], (res) => {
nameInput.value = res.name ?? "???"
})
```

The `get` method takes the following parameters:
* An array of the keys(`[name]`) you want to retrieve(the keys should match the keys in the `set` method).
* A callback function whose parameter represents an object containing key-value bindings of values initially stored with the `set` method.

For Example: Assuming the value stored in the `name` key is `john`

```javascript
    chrome.storage.sync.get(['name'], (res) => {
    console.log(res)
})


//logs

{name: 'john'}
```



We have successfully stored and retrieved the user input on the options page. Let's display the in on the popup page. In `popup.html` add.

```html
......
<body>
    <h1>My First Extension</h1>
    <h2 id="time"></h2>
+    <h2 id="name"></h2>
</body>
<script src="popup.js"></script>
</html>
```

In `popup.js` add.

```javascript
    const timeElement = document.getElementById('time')
    const nameElement = document.getElementById("name");
    const currentTime = new Date().toLocaleTimeString()
    timeElement.textContent = `The time is: ${currentTime}`


    chrome.action.setBadgeText({
        text: "TIME",
    }, () => {
        console.log('Finished setting badge text')
    }) 

    chrome.storage.sync.get(["name"], (res) => {
        const name = res.name ?? "???"
        nameElement.textContent = `Your name is: ${res.name}`
    })
```

In the preceding code, we select the element(`nameElement`) that will display our stored value, get the value from storage, and set it as the `textContent` of the selected element.

We can now view the value input in the options page on the popup page.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/fv2jmh8.gif)


 

## Background Scripts and Service Workers

As per the [documentation](https://developer.chrome.com/docs/extensions/mv2/background_pages/)
> Extensions are event-based programs used to modify or enhance the Chrome browsing experience. Events are browser triggers, such as navigating to a new page, removing a bookmark, or closing a tab. Extensions monitor these events in their background script, then react with specified instructions.

The background script is a Javascript file that runs in the background while your Chrome extension is installed. Service workers are always idle after a while;  learn more about service workers [here](https://developer.chrome.com/docs/workbox/service-worker-overview/).

Let's implement the background script for our extension:

Add the `"background"` field in our `manifest.json` file.

```javascript
{
  "manifest_version": 3,
  "name": "First Extension",
  "version": "1.0.0",
  "description": "My first extension",
  "icons": {
    "16": "icon.png",
    "48": "icon.png",
    "128": "icon.png"
  },
  "action": {
    "default_icon": {
        "16": "icon.png",
        "24": "icon.png",
        "32": "icon.png"
      },
      "default_title": "My Extension Action Title",
      "default_popup": "popup.html"
  },
  "options_page": "options.html",
  "permissions": ["storage"],
  "background": {
    "service_worker": "lbackground.js"
  }
}
```

Create a `background.js` file

```
📦 chrome extension basics
 ┣ 🎨 icon.png
 ┣ 📄 manifest.json
 ┣ 📄 popup.html
 ┣ 📄 popup.css
 ┣ 📄 popup.js
 ┣ 📄 options.html
 ┣ 📄 options.css
 ┣ 📄 options.js
 ┣ 📄 background.js
```

```javascript
    consol.log("Hello from the background script!");
```

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/1oZAvht.gif)


The above demo shows that after adding the background script, you will see the *inspect views:service worker* option when you go to the extensions page and refresh the extension. When we hit inspect, we will see that it shows the message we are logging to the console from the `background.js`.

It is important to note the following about the background script:

* The devtools environment that shows up when the `service worker` option is clicked on is not different from the one we see when the popup page is inspected, except in comparison to our popup, we don't the `Elements` tab because the background script is all Javascript(only Javascript related devtools are available).
* The `this` keyword doesn't point to the `window` object in the background script, and it points to the `ServiceWorkerGlobalScope` object(which means it's a service worker). So it's good to be aware that the service worker does not have the same functionality as a regular Javascript file you would have in an HTML document.

```javascript
/// background.js

console.log(this)

// logs

> ServiceWorkerGlobalScope
```

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/Vc7JbcM.png)

Now let's set up a timer functionality in our background script.

```javascript
/// background.js

let time = 0

setInterval(() => {
    time +=  1
    console.log(time)
}, 1000)
```

The preceding code increases the` time` variable every second via the `setInterval()` function.

 

## Chrome Alarms API

As per the [documentation](https://developer.chrome.com/docs/extensions/reference/alarms/)

> Use the `chrome.alarms` API to schedule code to run periodically or at a specified time in the future.

This means that the `chrome.alarms` API allows code to run inside a background script, even if the service worker is resting.

Let's enable the permissions for this API in our manifest file.

```javascript
/// manifest.json

......
"options_page": "options.html",
  "permissions": ["storage", "alarms"]  
  "background": {
   "service_worker": "background.js"   
  }
.....
```

Now let's re-implement the timer functionality using the `chrome.alarms` API in `background.js`.

```javascript
chrome.alarms.create({
    periodInMinutes: 1 / 60, 
})

chrome.alarms.onAlarm.addListener((alarm) => {
    chrome.storage.local.get(["timer"], (res) => {
        const time = res.timer ?? 0
        chrome.storage.local.set({
            timer: time + 1
        })
        console.log(time)
    })
})
```

The preceding code uses the [`chrome.alarms.create`](https://developer.chrome.com/docs/extensions/reference/alarms/#method-create) method to create an alarm that fires every second via the `periodInMinutes` property. Then the `onAlarm` method is used to listen and respond to the alarm. Finally, we use `chrome.storage` to set and increase the timer, then log it to the console.

 

## Chrome Notifications API

As per the [documentation](https://developer.chrome.com/docs/extensions/reference/notifications/)

> Use the `chrome.notifications` API to create rich notifications using templates and show these notifications to users in the system tray.

This means the `chrome.notifications` API is used to create desktop notifications for our extensions. However, it is important to note that the `background script` is a service worker in [manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/). On service workers, there is something called the [ServiceWorkerRegistration](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration) object. It has a built-in show notification function([`ServiceWorkerRegistration.showNotification()`](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration)) that displays notifications to users on a desktop.



Now, let's use the API to notify the user when a certain time period has passed. Let's enable the permissions for this API in our manifest file.

```javascript
/// manifest.json

......
"options_page": "options.html",
  + "permissions": ["storage", "alarms", "notifications"]  
  "background": {
   "service_worker": "background.js"   
  }
.....
```

In `background.js` 

```javascript
 chrome.alarms.create({
    periodInMinutes: 1 / 60, 
})

 chrome.alarms.onAlarm.addListener((alarm) => {
    chrome.storage.local.get(["timer"], (res) => {
        const time = res.timer ?? 0
        chrome.storage.local.set({
            timer: time + 1
        })
    if(time % 5 == 0) {
        this.registration.showNotification('My first Extension', {
            body: '1 second has passed',
            icon: 'icon.png',
          })
    }
    })
})
```

In the preceding code, we access the `showNotification()` function via the serviceWorker object(`this`). The function accepts two parameters—A string that represents the notification's title and an object that contains different options for configuring the notification. In our case, the notification title is *My first Extension*; our configuration object contains the `body` property(the text displayed on the notification body) and the `icon`(the notification icon). Our notification is wrapped around an `if` statement that triggers the notification every 5 seconds.

![--](https://blog.openreplay.com/images/chrome-extensions-for-beginners--part-1-basics/images/UupdmiG.gif)

Now refresh the extension page and wait 5 seconds; you will see the notification on your desktop.

 
 

## To be continued...

That's it for the first part of this two-part series. In the next part, we will be building a Pomodoro timer extension.

You can download this article's example code files from a [GitHub repository](https://github.com/KindEni/Chrome-Extension-Series).

In the meantime, please look at the **Read More** section below if you want to explore the concepts in this article a little more deeply.


## Read More
* [Chrome developer documentation](https://developer.chrome.com/docs/extensions/)
* [Manifest file reference](https://www.udemy.com/course/chrome-extension/learn/lecture/25763382#overview)
* [Chrome extension APIs reference](https://developer.chrome.com/docs/extensions/reference/)
* [Service worker fundamentals](https://developer.chrome.com/docs/workbox/service-worker-overview/)






[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)