Understanding Modern Chrome Extensions: A Developer’s Guide

February 25th, 2025

Introduction

Web extensions have become an essential tool for enhancing browser functionality, offering users customized experiences ranging from ad-blocking and password management to productivity tools and automation scripts. Whether you’re looking to build a simple extension to streamline your workflow or a feature-rich tool for a broader audience, understanding the development process is key. In this article, we’ll walk through the fundamentals of building a Chrome extension, covering its key components, setup, and best practices. We’ll also take a brief look at Manifest V3, Google’s latest update to the extension framework. We’ll dive into how it impacts development and the controversy surrounding it. Whether you’re a beginner or an experienced developer, this article will equip you with the knowledge to create and refine your own Chrome extension. Trust me, it’s easier than you think!

What is a Chrome Extension?

Before we can get started developing a Chrome Extension we should first define what a Chrome Extension is. A Chrome extension is a small software program that enhances the functionality of the Google Chrome browser by modifying or interacting with web pages and browser features. These extensions allow users to personalize their browsing experience, automate tasks, and introduce new capabilities that aren’t natively available in Chrome. 

Extensions serve a variety of purposes and can be found in many different categories. Some of the most common use cases include:

  • Ad Blockers: Prevent unwanted ads from displaying on web pages.
  • Productivity Tools: Enhance efficiency with features like tab management, note-taking, or task automation.
  • Password Managers: Store and autofill login credentials securely.
  • Development Scripts: Assist developers with tools for extracting color codes, running custom scripts on websites, and even analyzing and debugging web traffic.
  • Security & Privacy Tools: Block tracking scripts, enforce HTTPS, or prevent malicious websites from loading.

As you can see this is only the tip of the iceberg – the possibilities with Chrome Extensions are almost endless and you can tailor your extension to fit whatever your needs may be. By leveraging Chrome’s extension APIs, we can integrate extensions with browser features like bookmarks, tabs, and storage. These lightweight extensions run seamlessly in the background or as interactive popups, making Chrome extensions a powerful way to customize and improve the web browsing experience.

Key Components of a Chrome Extension

A Chrome extension is made up of several key components that work together to enhance the browser’s functionality. Each component has a specific role, whether it’s defining the extension’s behavior, handling background tasks, modifying web pages, or providing user interaction. Understanding these components is crucial for building a well-structured and efficient extension.

Manifest File: The Blueprint of Your Extension

At the core of every Chrome extension is the manifest file (manifest.json). This file serves as the extension’s blueprint, defining its metadata, permissions, background processes, and interactions with web pages. The manifest determines which browser APIs the extension can access and specifies how different scripts are loaded. It is particularly important in Manifest V3, the latest extension framework, as it enforces stricter security measures, such as requiring service workers instead of persistent background scripts. Without a properly configured manifest file, the extension simply won’t function.

Here’s a simple example:

{

  "manifest_version": 3,

  "name": "My Chrome Extension",

  "version": "1.0",

  "description": "A simple Chrome extension example.",

  "permissions": ["storage", "tabs"],

  "host_permissions": ["https://*/*"],

  "background": {

    "service_worker": "background.js"

  },

  "action": {

    "default_popup": "popup.html",

    "default_icon": "icon.png"

  },

  "content_scripts": [

    {

      "matches": ["https://*/*"],

      "js": ["content.js"]

    }

  ]

}

Background Scripts: The Brain Behind the Extension

Background scripts are one of the most critical components of a Chrome extension. They run independently of any specific webpage, allowing the extension to handle tasks that need to persist across browser sessions. These scripts listen for events such as browser actions, tab changes, and network requests, enabling the extension to respond dynamically without requiring direct user interaction. A lot of your core logic will live in these scripts and will include most of your listeners, API calls, and storage functions.

For example, if your extension needs to monitor when a user visits a particular website or automate a process in the background (like checking for updates or storing data), the background script is responsible for handling those operations. In Manifest V3, background scripts are replaced by service workers, which only run when needed and shut down when idle to improve efficiency. The bottom line here is that anything that’s related to your extension as a whole that isn’t specific to one page or tab, should be placed in your background script. For example, using a background script to periodically check for new data from an API and notify the user if an update is available. This can be particularly useful for extensions that monitor stock prices, weather updates, or other time-sensitive information.

The following is a simple example of how a background script can track visits to different web pages using Chrome’s Storage API. This script only increments the visit count when a user navigates to a predefined list of websites (e.g., washingtonpost.com, reddit.com, espn.com). Each visit is recorded and stored separately for each domain, allowing the extension to keep track of how often the user visits each tracked site. This is a basic implementation, but it demonstrates how you can build upon it to collect more detailed browsing data or integrate it with other extension features.

chrome.runtime.onInstalled.addListener(() => {

  chrome.storage.sync.set({ visits: {} }, () => {

    console.log("Visit tracking initialized.");

  });

});

chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {

  if (changeInfo.status === "complete" && tab.url) {

    const trackedSites = ["washingtonpost.com", "reddit.com", "espn.com"];

    const url = new URL(tab.url);

    if (trackedSites.some(site => url.hostname.includes(site))) {

      chrome.storage.sync.get("visits", (data) => {

        let visits = data.visits || {};

        visits[url.hostname] = (visits[url.hostname] || 0) + 1;

        chrome.storage.sync.set({ visits }, () => {

          console.log(`Visited ${url.hostname}: ${visits[url.hostname]} times`);

        });

      });

    }

  }

}); // background script

Content Scripts: The Bridge Between the Extension and Web Pages

While background scripts handle all your tasks behind the scenes, content scripts are just as important as they are responsible for interacting with web pages. These scripts are injected into specified sites and allow the extension to modify the page’s content, manipulate the DOM, or extract information. Content scripts essentially act as the extension’s way of integrating with the web. This is how extensions like ad blockers, theme customizers, and automation tools work to dynamically alter web pages to enhance user experience.

While content scripts are as important as background scripts, it’s important to note that content scripts do not have direct access to Chrome’s extension APIs. Instead, they communicate with the background script through message passing, ensuring a secure and structured way of exchanging information. This separation enhances security, preventing malicious extensions from directly manipulating browser functions.

For example, the following content script changes all page text to uppercase when a message is received from the background script:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { 

  if (message.action === "uppercaseText") {

      document.body.innerHTML = document.body.innerHTML.toUpperCase();

      sendResponse({ status: "Text converted to uppercase" });

  }

}); // content script

This next script listens for a message from the background script and, when triggered, modifies the DOM by transforming all text to uppercase. To activate this content script from the background script or popup, you would send a message like this:

chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {

  chrome.tabs.sendMessage(tabs[0].id, { action: "uppercaseText" }, (response) => {

      console.log(response.status);

  });

}); // background script

This demonstrates how background and content scripts work together, enabling extensions to interact with web pages while maintaining a secure and structured approach to executing actions.

Permissions & APIs: Controlling What Your Extension Can Do

For your extension to function properly, it must declare permissions in the manifest file, as shown previously. Permissions determine what browser data or functionality your extension can access, such as reading tabs, modifying web pages, or storing user settings. Chrome’s extension APIs provide a powerful set of tools for developers, allowing them to work with bookmarks, history, storage, and even intercept network requests.

When a user installs a Chrome extension, they are shown a prompt listing the permissions the extension requires, allowing them to review what data and browser features it will have access to. Because of this, permissions should be used judiciously – requesting excessive permissions can deter users from installing your extension and may even lead to rejection from the Chrome Web Store. To improve security and user trust, Chrome provides optional permissions, allowing extensions to request access only when needed rather than requiring all permissions upfront during installation.

For example, if your extension needs access to a user’s bookmarks and interact with their tabs, you would declare it in the manifest.json file like this:

"permissions": ["bookmarks", “tabs”]

Now you’re able to use these APIs in your background script to fetch a user’s bookmarks or to view tab data. For example:

chrome.bookmarks.getTree((bookmarks) => {

  console.log("User's bookmarks:", bookmarks);

});

chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {

  console.log("Current tab:", tabs[0].url);

}); // background script

It’s important to note that using Chrome’s APIs like this is only valid in background scripts. As mentioned earlier, if a content script needs this information, it must communicate with the background script using the Chrome Messages API to request the data. For example:

chrome.runtime.sendMessage({ action: "getBookmarks" }, (response) => {

  console.log("Bookmarks:", response.bookmarks);

}); // content script

 For a full list of available APIs and permissions, refer to the official Chrome Extensions API documentation.

Popup UI: Providing User Interaction

Many Chrome extensions offer a popup UI, a small interactive interface that appears when the extension icon is clicked. This UI allows users to interact with the extension without navigating away from their current webpage. It can display settings, provide shortcuts, or execute commands directly within the browser. Popups are often used for quick access to features, such as enabling or disabling the extension, running a function, or showing real-time data. Since popups are part of the extension’s front-end experience, they are typically built using standard HTML, CSS, and JavaScript.

A great example of a popular Chrome extension is uBlock Origin. While its interface appears simple, it offers powerful functionality, allowing users to interact seamlessly with both the extension and the browser in various ways. The only constraint to keep in mind is that you are developing a Popup for an extension, so it’s best to keep it lightweight and focused, avoiding unnecessary complexity.  If your extension requires more advanced functionality or a richer user interface, consider providing dedicated static pages that users can access from the extension for a more configurable and immersive experience.

Manifest V3: Understanding the Impact and Controversy

What is Manifest V3?

Manifest V3 is Google’s latest update to the Chrome extension framework and API. It is designed to enhance security, improve performance, and reduce resource consumption. While these updates improve security and performance, they also introduce new challenges for developers, especially those used to the flexibility of Manifest V2. The most significant change is the replacement of persistent background scripts with service workers, forcing extensions to adopt an event-driven approach where scripts only run when needed and shut down when idle. Additionally, Manifest V3 removes the webRequest API’s blocking capabilities, replacing it with the declarativeNetRequest API, which restricts developers to predefined filtering rules instead of dynamically intercepting network traffic.

Why is Manifest V3 Controversial?

While Google positions Manifest V3 as a step toward a more secure and efficient browsing experience, it has faced strong criticism, especially from developers of ad blockers, privacy tools, and automation extensions. The biggest concern is the removal of dynamic request blocking, which has made ad blockers less effective by limiting their ability to inspect and modify web requests in real time. Many argue this benefits advertisers at the expense of user control. Furthermore, the transition to service workers has made developing complex extensions more challenging, as scripts no longer persist and must be carefully structured to handle tasks efficiently. In fact, browser vendors like Mozilla Firefox have expressed plans to retain certain Manifest V2 functionalities, providing an alternative for developers who find Manifest V3 too restrictive. Additionally, other Chromium-based browsers like Microsoft Edge and Brave can offer more flexibility for extensions, making them viable options for developers who need greater control. It’s important to remember that these changes aren’t all bad. Manifest V2 had several cited security risks and performance issues that Manifest V3 solves. Google is largely aiming to prevent malicious extensions and to also create a more privacy-conscious ecosystem. However, many developers feel these restrictions diminish the flexibility that made Chrome extensions so powerful.

Setting Up Your First Chrome Extension

Now comes the fun part: building the extension from the ground up. Building a Chrome extension might seem daunting, but it’s easier than you think. In this section, we’ll walk through the fundamental steps required to create a basic extension, from organizing your files and adding functionality. By the end, you’ll have a working extension running in Chrome!

Step 1: Organizing Your Project Structure

Before writing any code, it’s important to properly structure your extension’s files. A well-organized project makes it easier to manage and scale your extension over time. At a minimum, your Chrome extension should include the following files:

📁 my-extension/
├── 📄 manifest.json → Defines extension metadata and permissions.
├── 📄 background.js → Handles background tasks and events.
├── 📄 content.js → Interacts with web pages and modifies the DOM.
├── 📂 popup/ → (Optional) Contains UI files if you’re adding a popup interface.
│ ├── 📄 popup.html → The extension’s interactive popup UI.
│ ├── 📄 popup.js → Handles popup actions and user interaction.
├── 📂 icons/ → (Optional) Stores extension icons for different sizes.
├── 📄 styles.css → (Optional) Styles for popup or content scripts.

This structure ensures that your manifest file, scripts, and assets are properly organized, making development and debugging much easier.

Step 2: Adding Functionality

We can set up the manifest file as referenced earlier in the article. Once your manifest file is set up, it’s time to add functionality using background and content scripts. The background script manages tasks that need to persist, such as listening for user interactions or handling events. Here’s a simple background.js script that logs when the extension is installed:

chrome.runtime.onInstalled.addListener(() => {

  console.log("Extension installed successfully!");

});

This script will run in the background and print a message to the console when the extension is first installed.

Next, we want to inject our content script. The content scripts will allow your extension to modify web pages and their DOM content. Here’s an example contentScript.js file that changes the background color of a webpage when loaded:  

document.body.style.backgroundColor = "lightblue";

Now when a user visits any webpage, this script will automatically inject and modify the page’s appearance. This is the same technique used by mainstream extensions, albeit more rudimentary.

Step 3: Putting Everything Together

Now that your files are set up, it’s time to load the extension into Chrome and test it!

  1. Open Chrome and navigate to chrome://extensions/.
    • This will lead you to your Chrome Extension Console. You can load development builds of your extension here
  2. Enable Developer Mode by toggling the switch in the top right corner.
  3. Click “Load Unpacked” and select the my-extension/ folder.
    • You only need to select the folder that contains your manifest.json file
  4. The extension will now appear in your list! Click on the extension icon to test the popup (if added).
    • You can also view any errors that may come up or even open a debug console by selecting the “Service Worker” hyperlink on your extension

Step 4: Debugging

Debugging your Chrome extension is an essential part of development. You can inspect errors and logs by opening Developer Tools (F12) and going to Console, where the content scripts will output messages. For background scripts, navigate to chrome://extensions/, find your extension, and click “Service Worker.” This will open up its own isolated developer console where you can view logs, network requests, and check for issues. If the extension isn’t behaving as expected, double-check your permissions in manifest.json, ensure scripts are correctly referenced, and use console.log to help track your execution flow. To see changes on your extension as you develop, reloading the extension after making changes ensures any changes you make in your code take effect.

Final Thoughts

Now that wasn’t so bad was it? You’ve just built and loaded your first Chrome extension. From here, you can expand its functionality by adding a popup UI, storage features, and API integrations. As you continue developing, consider optimizing performance, using event-driven background scripts, and implementing message passing between components. Whether you’re creating a productivity tool, security feature, or a simple browser enhancement, there are plenty of ways to refine and improve your extension. Taking the time to explore Chrome’s extension APIs further will help you build more robust and useful applications.

Helpful Resources:

At Solution Street, we’ve developed multiple Chrome plugins for our clients. If you’re looking for expert help in building one, feel free to reach out!