There are several ways to build a Chrome extension, each with distinct capabilities. which approach would you choose?
Before start
Every Chrome extension starts with a manifest.json file, which serves as the blueprint for the extension. This file defines essential metadata such as the extension’s name, version, permissions, and the scripts or pages it will use.
According to the official announcement from Chrome for Developers, starting from June 2025 with Chrome version 139, only Manifest V3 will be supported, while Manifest V2 will be phased out.
We will explore the whole way based on the Manifest V3 standard. Let's go!
1. Popup
One of the most common ways to interact with an extension is through a popup that appears when the user clicks the extension icon in the toolbar. This popup is a small HTML page defined in the manifest and typically contains a simple interface for quick interactions.
directory structure :
popup
├── icon.png
├── manifest.json
└── popup.html
manifest.json
{
"manifest_version": 3,
"version": "0.0.1",
"name": "my-extension",
"description": "describe here",
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
}
}
In manifest.json, in addition to specifying the extension's version, name, and description, it also configures the popup's HTML file (popup.html) through the action.default_popup field and the toolbar icon via action.default_icon.
popup.html
<!DOCTYPE html>
<html>
<body>
<div style="padding: 16px">
<h3>My Extension</h3>
</div>
</body>
</html>
The popup.html forms the foundation of its user interface, it may contain references to supporting CSS and JavaScript files that together enable the extension's functionality and interactivity.
interaction:
Click the extension's toolbar icon, its popup interface will appear.
2. New Tab
Instead of a popup, some extensions may prefer to open a full tab when clicked. This approach is useful when the extension requires more space for complex interactions.
To achieve this, the extension can listen for clicks on the browser action and then programmatically open a new tab. The background.js script handles this logic.
directory structure:
newtab
├── background.js
├── icon.png
├── index.html
└── manifest.json
manifest.json
{
"manifest_version": 3,
"version": "0.0.1",
"name": "my-extension",
"description": "describe here",
"action": {
"default_icon": "icon.png"
},
"background": {
"service_worker": "/background.js"
}
}
In manifest.json, background.js is specified by the background.service_worker field.
background.js
chrome.action.onClicked.addListener(function(tab) {
chrome.tabs.create({'url': chrome.runtime.getURL('index.html')},
function(tab) {
// Tab opened.
});
});
In background.js, it listens for click events on the icon and invokes the api of chrome.tabs.create to open a new tab. The new tab's URL is generated using chrome.runtime.getURL('index.html'), which automatically resolves the full extension-relative path for the specified HTML file (index.html).
index.html
<!DOCTYPE html>
<html>
<body>
<div style="padding: 16px">
<h3>index page</h3>
</div>
</body>
</html>
index.html can accommodate significantly more content than the popup window since it functions as a complete standalone page, offering full flexibility for your implementation needs.
interaction:
Click the toolbar icon to open a new tab.
3. Sidebar
Chrome also supports a less common but interesting option: the sidebar (or side panel). This opens a vertical panel next to the current webpage, perfect for tools that need to stay visible while browsing — like document outlines or live translators.
To use a sidebar, the manifest.json needs the "sidePanel" permission, and the extension controls it using the chrome.sidebarAction API. Unlike popups, sidebar stay open until the user closes them, making them great for long-term tasks.
directory structure:
sidebar
├── background.js
├── icon.png
├── manifest.json
└── side_panel.html
manifest.json
{
"manifest_version": 3,
"version": "0.0.1",
"name": "plugin-test",
"description": "describe here",
"side_panel": {
"default_path": "side_panel.html"
},
"action": {
"default_icon": "icon.png"
},
"background": {
"service_worker": "/background.js"
},
"permissions": [
"sidePanel"
]
}
In manifest.json, the sidebar's HTML file (side_panel.html) is declared through the side_panel.default_path field. Additionally, the "sidePanel" permission must be included in the permissions array to enable sidebar functionality.
background.js
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: true })
.catch((error) => console.error(error));
Click events will be listened from background.js to control the sidebar.
side_panel.html
<!DOCTYPE html>
<html>
<body>
<div style="padding: 16px">
<h3>side panel</h3>
</div>
</body>
</html>
side_panel.html can be utilized much like a full tab page, with both providing sufficient possibility for feature-rich interfaces.
interaction:
Click the toolbar icon to toggle the sidebar open and closed.
4. Embedding Widget
Some extensions modify web pages by embedding small interactive widgets directly into them. Content scripts can add floating toolbars or translation bubbles by modifying current page’s DOM. These scripts run inside the webpage but can’t use most Chrome extension APIs due to security restrictions.
directory structure:
widget
├── manifest.json
└── widget.js
manifest.json
{
"manifest_version": 3,
"version": "0.0.1",
"name": "my-extension",
"description": "describe here",
"content_scripts": [
{
"matches": ["https://test.com/*"],
"js": ["widget.js"]
}
]
}
The content_scripts field in manifest.json defines which scripts get injected into matching web pages, specifying the JavaScript (or CSS) files to inject and the URL patterns that determine when injection occurs.
widget.js
const widget = document.createElement('div');
widget.id = 'extensionWidget';
widget.style.position = 'fixed';
widget.style.top = '15px';
widget.style.right = '160px';
widget.style.padding = '10px';
widget.style.background = 'blue';
widget.style.borderRadius = '8px';
widget.style.zIndex = '9999';
widget.innerHTML = `<h4 style="color: white">Extension Widget</h4>`;
document.body.appendChild(widget);
Once the extension is installed and enabled, widget.js will be injected and executed in all pages matching the https://test.com/* URL pattern, and the "Extension Widget" widget will be displayed on the top-right corner.
This method allows for seamless integration with web pages but requires careful handling of existing page elements to avoid conflicts.
Summary
Each approach has its use cases - popups for quick interactions, new tabs for complex UIs, injected sidebars for persistent tools, and embedded widgets for page integration. The best choice depends on your extension's specific requirements and user experience goals.
Remember to always request the minimal necessary permissions and test your extension thoroughly, as Manifest V3 has stricter security requirements than its predecessor.
I just developed a Chrome extension called Bookmark Dashboard, a bookmark management tool, and I’d love to share it with fellow bookmark enthusiasts!
Thanks for reading!
Top comments (0)