How to Build a Chrome Extension in 2026 (Step-by-Step)
How to Build a Chrome Extension in 2026 (Step-by-Step)
Chrome extensions are one of the most accessible entry points into software development. You do not need a server, a framework, or a complicated build system. A Chrome extension is just HTML, CSS, and JavaScript — packaged with a manifest file that tells the browser what your extension does and what permissions it needs. If you can write basic web code, you can build a Chrome extension in an afternoon.
This tutorial walks you through the entire process from an empty folder to a working extension installed in your browser. Instead of building a contrived example, we will build something real: a keyboard-shortcut URL copier — the same concept behind Ctrl+Shift+C, a production extension used by thousands of people to copy URLs with a single keypress. By the end of this guide, you will understand how Chrome extensions work, how Manifest V3 structures them, and how to publish your own.
What You Need Before You Start
The prerequisites for building a Chrome extension are minimal:
- A text editor. VS Code, WebStorm, Sublime Text, or even Notepad — anything that edits plain text files.
- Google Chrome. Any recent version. Chrome's extension development tools are built into the browser.
- Basic JavaScript knowledge. You do not need to be an expert. If you understand variables, functions, event listeners, and the DOM, you have enough.
That is it. No Node.js installation required, no package manager, no build tools. You can add those later for more complex extensions, but they are not necessary to build a Chrome extension from scratch.
Understanding Manifest V3: The Foundation of Every Extension
Every Chrome extension starts with a manifest.json file. This is the configuration file that tells Chrome everything about your extension — its name, version, permissions, icons, and which scripts to load. Since 2023, all new Chrome extensions use Manifest V3, which replaced the older Manifest V2 with a more secure and performant architecture.
Here is the manifest for our URL copier extension:
{
"manifest_version": 3,
"name": "URL Copier",
"version": "1.0.0",
"description": "Copy the current tab URL with a keyboard shortcut.",
"permissions": ["activeTab"],
"commands": {
"copy-url": {
"suggested_key": {
"default": "Ctrl+Shift+C",
"mac": "Command+Shift+C"
},
"description": "Copy current tab URL to clipboard"
}
},
"background": {
"service_worker": "background.js"
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
Let us break down the key parts:
manifest_version: 3 — This declares the extension uses Manifest V3. Chrome will reject extensions that try to use Manifest V2 for new submissions.
permissions: ["activeTab"] — This is the principle of least privilege in action. The activeTab permission only gives the extension access to the tab the user is currently viewing, and only when triggered by a user action. It cannot read other tabs, browsing history, or any data the user did not explicitly interact with.
commands — This registers a keyboard shortcut with Chrome. The browser handles the key binding natively, which means it works even before any extension code runs. When the user presses the shortcut, Chrome fires an event that our background script listens for.
background.service_worker — In Manifest V3, background scripts run as service workers instead of persistent background pages. Service workers are event-driven — they wake up when needed and shut down when idle. This is a major performance improvement over Manifest V2, where background pages could consume memory indefinitely.
Step 1: Create the Project Structure
Create a new folder for your extension and set up the following file structure:
url-copier/
├── manifest.json
├── background.js
└── icons/
├── icon16.png
├── icon48.png
└── icon128.png
That is the entire project. Three files and three icons. When people ask how to build a Chrome extension, they expect a complex setup — but a focused extension really is this simple.
Copy the manifest from the section above into manifest.json. For the icons, create simple PNG files at 16x16, 48x48, and 128x128 pixels. You can use any image editor, or generate them with an online tool. The 16px icon appears in the toolbar, the 48px icon appears on the extensions management page, and the 128px icon appears in the Chrome Web Store listing.
Step 2: Write the Background Service Worker
The background service worker is where your extension logic lives. For our URL copier, it needs to do three things: listen for the keyboard shortcut, read the current tab URL, and copy it to the clipboard.
Create background.js with the following code:
chrome.commands.onCommand.addListener(async (command) => {
if (command === 'copy-url') {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true,
});
if (tab?.url) {
await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: (url) => {
navigator.clipboard.writeText(url);
},
args: [tab.url],
});
}
}
});
Here is what each part does:
chrome.commands.onCommand.addListener — This listens for the keyboard shortcut we defined in the manifest. When the user presses Ctrl+Shift+C (or Cmd+Shift+C on Mac), Chrome fires this event with the command name.
chrome.tabs.query — This queries for the active tab in the current window. The activeTab permission ensures we can only access the tab the user is looking at.
chrome.scripting.executeScript — In Manifest V3, service workers do not have direct access to the clipboard API (since they run in a background context without a DOM). So we inject a small function into the active tab that calls navigator.clipboard.writeText(). This is the standard web clipboard API — the same one any website can use.
args: [tab.url] — We pass the URL as an argument to the injected function. This keeps the logic clean: the background script reads the tab URL using Chrome APIs, and the injected script writes it to the clipboard using web APIs.
This is the complete logic for a functional URL copier. Under 20 lines of code. The production version of Ctrl+Shift+C adds visual feedback, error handling, and onboarding — but the core mechanism is exactly this simple.
Step 3: Load and Test Your Extension
Now that you have the manifest and background script, load your extension into Chrome for testing:
- Open Chrome and navigate to
chrome://extensions/. - Enable Developer mode using the toggle in the top-right corner.
- Click Load unpacked and select your
url-copierfolder. - Your extension appears in the list with its name, version, and a unique ID.
To test it:
- Open any website (extensions do not work on
chrome://pages or the Chrome Web Store). - Press Ctrl+Shift+C (or Cmd+Shift+C on Mac).
- Paste somewhere — you should see the URL of the page you were on.
If it does not work, check for errors by clicking the Errors button on your extension's card in chrome://extensions/. Common issues include typos in the manifest, incorrect file paths, or permission mismatches.
Hot reloading. When you make changes to your code, click the refresh icon on your extension's card in chrome://extensions/. For manifest changes, you may need to remove and re-load the extension. Unlike web development, there is no automatic hot reload for extensions — you have to manually trigger it.
Step 4: Add Visual Feedback
A URL copier that silently copies without any confirmation is functional but not user-friendly. People need to know the copy succeeded. Let us add a brief visual notification.
Update background.js to show feedback after copying:
chrome.commands.onCommand.addListener(async (command) => {
if (command === 'copy-url') {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true,
});
if (tab?.url) {
await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: (url) => {
navigator.clipboard.writeText(url);
const toast = document.createElement('div');
toast.textContent = 'URL copied!';
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #333;
color: #fff;
padding: 12px 20px;
border-radius: 8px;
font-family: system-ui, sans-serif;
font-size: 14px;
z-index: 2147483647;
transition: opacity 0.3s;
`;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
setTimeout(() => toast.remove(), 300);
}, 1500);
},
args: [tab.url],
});
}
}
});
The injected script now creates a small toast notification that appears in the top-right corner, stays visible for 1.5 seconds, then fades out and removes itself from the DOM. The z-index is set to the maximum value to ensure it appears above any page content.
This is a common pattern when you build a Chrome extension that interacts with page content — inject a small, self-contained piece of UI that does its job and cleans itself up.
Step 5: Prepare for the Chrome Web Store
Once your extension works locally, publishing it to the Chrome Web Store makes it available to everyone. Here is what you need:
Developer account. Register at the Chrome Web Store Developer Dashboard. There is a one-time $5 registration fee.
Store listing assets. You need:
- At least one screenshot (1280x800 or 640x400 pixels)
- A detailed description (mention your primary function, key features, and privacy practices)
- A privacy policy (can be a simple page explaining what data you do and do not collect)
Package your extension. Zip the contents of your extension folder (not the folder itself — the files inside it). Upload the zip to the Developer Dashboard.
Review process. Google reviews all new extensions and updates. Simple extensions with minimal permissions — like a URL copier using only activeTab — typically get approved within one to three business days. Extensions requesting broad permissions may take longer and face additional scrutiny.
Privacy practices disclosure. The Chrome Web Store requires you to disclose what user data your extension collects and how it is used. For a tiny Chrome extension that collects nothing, this is straightforward — declare that you collect no data. Zero data collection is not just good ethics, it is a competitive advantage. Users increasingly check this section before installing, and "This developer declares that it does not collect or use your data" is the most reassuring statement possible. This is exactly the approach Ctrl+Shift+C takes.
Manifest V3 vs. Manifest V2: What Changed
If you are reading older tutorials online, many still reference Manifest V2. Google fully transitioned to Manifest V3 and no longer accepts Manifest V2 submissions. Here are the key differences you need to know when you build a Chrome extension in 2026:
| Feature | Manifest V2 | Manifest V3 |
|---------|------------|-------------|
| Background scripts | Persistent background pages | Event-driven service workers |
| Content security | Relaxed CSP, allowed remote code | Strict CSP, no remote code execution |
| Network requests | webRequest blocking API | declarativeNetRequest rules |
| Permissions | Often broadly requested upfront | Designed around activeTab and just-in-time access |
| Performance | Background pages consumed memory | Service workers are terminated when idle |
The biggest practical change is the shift from background pages to service workers. In Manifest V2, your background script ran as a persistent page that stayed in memory as long as the browser was open. In Manifest V3, the service worker starts when an event fires and shuts down after a period of inactivity. This means you cannot rely on global state — any data you need to persist must go into chrome.storage or chrome.storage.session.
The declarativeNetRequest API replaced the powerful but abusable webRequest blocking API. Instead of running arbitrary JavaScript to intercept and modify network requests, extensions now declare rules in JSON format. This is more restrictive but significantly more secure — extensions cannot silently inspect or modify your web traffic.
These changes make Manifest V3 extensions faster, safer, and more transparent. For focused, single-purpose extensions, the migration is usually straightforward. For complex extensions, it may require rethinking parts of the architecture.
Common Mistakes When Building Your First Chrome Extension
After building Ctrl+Shift+C and going through the entire development-to-store-listing process, here are the most common pitfalls to avoid:
Requesting too many permissions. Every permission you request appears on the installation prompt. Users see "This extension can: Read and change all your data on all websites" and close the tab. Only request what you actually need. activeTab is almost always sufficient for extensions that respond to user actions. If you want to build something people actually install, keep permissions minimal.
Not handling chrome:// pages. Extensions cannot run on Chrome internal pages like chrome://extensions, chrome://settings, or chrome://newtab. Your code needs to handle these gracefully — check that the tab URL exists and is not a restricted page before trying to interact with it.
Forgetting that service workers shut down. In Manifest V3, your background service worker will be terminated after about 30 seconds of inactivity. If you set a variable in your service worker and expect it to be there five minutes later, it will not be. Use chrome.storage.session for temporary state or chrome.storage.local for persistent data.
Ignoring the content security policy. Manifest V3 enforces a strict content security policy. You cannot load remote scripts, use eval(), or inline event handlers in your extension pages. All JavaScript must be in separate files referenced from your HTML.
Skipping error handling for clipboard operations. The clipboard API can fail — for example, if the document is not focused or if the page has a restrictive permissions policy. Always wrap clipboard operations in try-catch blocks.
Not testing across operating systems. Keyboard shortcuts behave differently on Windows, Mac, and Linux. The Ctrl key on Windows maps to Command on Mac, and some key combinations are reserved by the operating system. Define platform-specific defaults in your manifest and test on every platform you support.
For more on what makes a Chrome extension genuinely useful and lightweight, see Tiny Chrome Extensions That Actually Make a Difference.
Taking Your Extension Further
The URL copier we built is a complete, functional extension — but there is plenty of room to grow. Here are some directions to explore as you learn more about Chrome extension development:
Popup UI. Add a popup that appears when the user clicks the extension icon. This is an HTML page defined in the manifest under action.default_popup. Use it for settings, format options, or alternative copy modes (like copying the URL as a Markdown link).
Options page. Create an options page where users can configure behavior — custom keyboard shortcuts, URL format templates, or copy confirmation preferences. Define it in the manifest under options_page.
Content scripts. Inject JavaScript into specific websites to modify their behavior. Content scripts can read and modify the DOM of the page they are injected into. Use the content_scripts key in the manifest with URL match patterns to control where they run.
Storage API. Use chrome.storage.local or chrome.storage.sync to persist user settings. The sync variant automatically syncs data across the user's devices if they are signed into Chrome.
Context menus. Add a right-click context menu item using chrome.contextMenus. This gives users an alternative way to trigger your extension's functionality without remembering a keyboard shortcut.
Each of these features is well-documented in the Chrome Extensions documentation. Start with the basic version, get it working, and then add one feature at a time. That is how every good extension is built — the same approach behind Ctrl+Shift+C, which started as a simple URL copier and refined from there. For more on building a keyboard-driven development workflow, see 10 Chrome Keyboard Shortcuts Every Developer Should Know.
Frequently Asked Questions
How hard is it to build a Chrome extension? Not hard at all. If you know basic HTML, CSS, and JavaScript, you can build a Chrome extension in a few hours. The manifest file handles configuration, Chrome provides well-documented APIs, and you can test changes instantly by loading the extension locally. Simple extensions like a URL copier require fewer than 30 lines of logic.
Do I need to know React or TypeScript to build a Chrome extension? No. Chrome extensions are built with plain HTML, CSS, and JavaScript. You can use frameworks and TypeScript if you want — many complex extensions do — but they are optional. For focused, single-purpose extensions, vanilla JavaScript is often the better choice because it keeps the codebase small and eliminates build complexity.
How much does it cost to publish a Chrome extension? There is a one-time $5 registration fee for a Chrome Web Store developer account. After that, publishing and updating extensions is free. There are no ongoing fees, revenue sharing requirements, or hosting costs — Google hosts the extension files.
How long does Chrome Web Store review take? For simple extensions with minimal permissions, review typically takes one to three business days. Extensions requesting broad permissions or using complex features may take longer. Updates to existing extensions are usually reviewed faster than initial submissions.
What is the difference between Manifest V2 and Manifest V3?
Manifest V3 replaced persistent background pages with event-driven service workers, enforced a stricter content security policy, and introduced declarativeNetRequest to replace the webRequest blocking API. These changes make extensions faster, safer, and more transparent. All new extensions must use Manifest V3 — Google no longer accepts Manifest V2 submissions.
Can I build a Chrome extension that works on Edge and Brave? Yes. Microsoft Edge, Brave, Vivaldi, Arc, and all Chromium-based browsers support Chrome extensions natively. An extension you build for Chrome will work on these browsers without modification. Users can install it directly from the Chrome Web Store.
How do I make money from a Chrome extension? Common monetization strategies include freemium models (free core features, paid premium features), one-time purchases, or subscriptions. However, many of the best extensions — including Ctrl+Shift+C — are completely free. If your goal is learning or building a portfolio project, focus on making something genuinely useful first. Monetization can come later.
Start Building
You now have everything you need to build a Chrome extension from scratch. A manifest file to declare your extension's capabilities. A service worker to handle events. A content script to interact with the page. And a clear path to publishing on the Chrome Web Store.
The best extensions solve small, specific problems exceptionally well. They do not try to be platforms. They do not request permissions they do not need. They respect the user's browser, privacy, and attention. Start with one idea, build the simplest version that works, and refine from there.
Your first extension is twenty lines of code and an afternoon away. Open a text editor, create a manifest.json, and start building.
Try Ctrl+Shift+C
Copy any URL with one keyboard shortcut. Free forever, no data collected.