
September 08, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Building a Chrome Extension in 2026 is a practical way to ship a small product without a full mobile app or SaaS rebuild. You get direct access to the browser tab, bookmarks, and selected text. You also inherit Google's security model, which changed sharply when Manifest V2 support ended. This guide walks through Manifest V3 architecture, file layout, permissions, messaging, testing, and Chrome Web Store submission using patterns that hold up on real client projects.
What does building a Chrome Extension in 2026 require?
Every extension ships as a folder of static files loaded by Chrome. There is no build step unless you add one. The browser reads manifest.json first. That file declares version, permissions, scripts, and UI entry points. Google enforces Manifest V3 for all new listings. Older MV2 extensions were removed from the store in 2024.
If you already ship web applications for clients, an extension often complements the main product. Think form autofill for internal CRMs, price checkers for eCommerce staff, or a sidebar that talks to your Laravel REST API. The stack is HTML, CSS, and JavaScript. Node.js 26 LTS is useful locally for bundling with Vite 8.x, but the runtime in Chrome is plain JS.
Start with the smallest useful surface. One content script on one domain beats a broad <all_urls> permission that triggers review delays and user distrust. On production systems I maintain, extensions stay scoped to known hostnames—the same discipline you apply when locking down CORS on an API.
Minimum file structure
Create a project folder with these files. Names are conventional, not mandatory.
manifest.json— extension metadata and registrationbackground.js— event-driven service workercontent.js— runs inside matched web pagespopup.html+popup.js— toolbar popup UIicons/— 16, 48, and 128 px PNG assets
How do you scaffold a Manifest V3 Chrome extension?
Copy this starter manifest.json. It targets a single host pattern so Chrome prompts for limited access during install.
{
"manifest_version": 3,
"name": "Tab Notes",
"version": "1.0.0",
"description": "Save notes per tab on example.com",
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
"action": {
"default_popup": "popup.html",
"default_title": "Tab Notes"
},
"background": {
"service_worker": "background.js",
"type": "module"
},
"content_scripts": [
{
"matches": ["https://example.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}
],
"permissions": ["storage"],
"host_permissions": ["https://example.com/*"]
}
The service worker in background.js wakes on events and sleeps when idle. It cannot touch the DOM. Persist state with chrome.storage, not window.localStorage.
chrome.runtime.onInstalled.addListener(() => {
console.log('Extension installed');
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'GET_NOTE') {
chrome.storage.local.get([message.tabKey], (result) => {
sendResponse({ note: result[message.tabKey] ?? '' });
});
return true;
}
});
Content scripts share the DOM but not the page's JavaScript variables. They communicate through chrome.runtime.sendMessage. Validate payloads before acting on them. Treat untrusted page data like any external input—a lesson that overlaps with security practices for web developers in 2026.
const tabKey = `note:${location.hostname}:${location.pathname}`;
chrome.runtime.sendMessage({ type: 'GET_NOTE', tabKey }, (response) => {
if (response?.note) {
document.title = `[Note] ${document.title}`;
}
});
Load the extension unpacked: open chrome://extensions, enable Developer mode, click Load unpacked, and select your folder. Reload the extension after every code change. Use the service worker link on the card to inspect background logs.
How does messaging work between extension parts?
Extension components run in separate contexts. The popup closes when the user clicks away. The content script dies on navigation unless you inject again. Only the service worker (briefly) and chrome.storage survive across sessions.
Pick a messaging pattern early. One-way fire-and-forget suits analytics. Request-response needs return true in the listener when you call sendResponse asynchronously. Long-lived ports fit streaming or frequent updates between popup and background.
When an extension calls your backend, reuse API conventions from REST API design best practices. Send a static API key only if you accept the leak risk—any user can read extension source from the installed package. Prefer OAuth with chrome.identity.launchWebAuthFlow or short-lived tokens tied to the user's account.
Debug message failures systematically. Open DevTools on the page for content-script logs. Open the service worker inspector for background errors. A silent failure usually means the listener returned before sendResponse ran.
What changed from Manifest V2 to V3?
Manifest V3 is not a rename. It changes where code runs and what network calls look like. Extensions that relied on persistent background pages or blocking webRequest needed rewrites.
| Area | Manifest V2 | Manifest V3 (2026) |
|---|---|---|
| Background | Persistent background page | Ephemeral service worker |
| Remote code | Often tolerated in practice | Prohibited—all logic bundled locally |
| Network blocking | webRequestBlocking | declarativeNetRequest rules only |
| Host access | Often bundled in permissions | Split into host_permissions |
| Store review | Lighter on broad permissions | Stricter justification required |
Ad blockers and privacy tools were hit hardest. Most business extensions—sidebar panels, scrapers, form helpers—map cleanly to MV3 if you drop blocking hooks. For declarative rules, start from the declarativeNetRequest API reference and keep rule sets small.
How do you connect a Chrome extension to your web app or API?
Most useful extensions are front ends for data elsewhere. A directory assistant might call the same JSON endpoints as your main site. A legal-tech internal tool might pull matter metadata from a Laravel backend—the same integration mindset as client portal projects where document workflows live behind authenticated APIs.
Authentication options
- OAuth 2.0 via chrome.identity — best for multi-user SaaS; users sign in through a familiar browser flow.
- Session cookie on allowed domain — content scripts on your own origin can sometimes read session state; fragile if cookies are HttpOnly.
- API token pasted in options page — acceptable for internal tools with a tiny user base.
Store secrets in chrome.storage.session when you do not need them after browser restart. Never embed production admin keys in the package. Anyone can unzip a CRX file.
Validate JSON responses before rendering HTML into the popup. Use your JSON formatter tool while prototyping payloads. Test edge cases with the regex tester when parsing page text in content scripts.
If the extension transforms user-selected text—dates, currency, romanized Nepali—keep conversion logic in a shared npm module. Build it with Vite 8.x and import the bundle into both your web app and extension. One source of truth beats duplicated functions across codebases.
CORS and host permissions
host_permissions lets the service worker call listed origins without a browser CORS preflight block from the extension context. Your API still controls authorization. Return 401 for bad tokens. Log abuse server-side the same way you would for mobile clients.
For extensions that interact with third-party sites you do not own, you cannot bypass their CSP or CORS. Read the DOM from content scripts and send sanitized text to your backend instead of injecting remote scripts into the page.
How do you test, package, and publish to the Chrome Web Store?
Testing starts local and stays local longer than you expect. Write a manual checklist: install fresh, upgrade from prior version, exercise every permission string, and confirm uninstall removes storage if you promise that in the privacy policy.
Automate packaging with a simple script. Zip the folder excluding .git, node_modules, and source maps you do not want public.
#!/bin/bash
cd extension-src
zip -r ../tab-notes-v1.0.0.zip . \
-x "*.git*" -x "node_modules/*" -x "*.map"
Register a Chrome Web Store developer account (one-time fee, currently USD 5). Upload the ZIP in the developer dashboard. Fill listing copy, screenshots, and a privacy policy URL. Google reviews most extensions within a few days. Rejections often cite broad permissions or missing disclosure for data collection.
Declare single purpose clearly in the listing. If your extension saves notes, do not also request tabs, history, and clipboard unless each permission supports that one purpose. Tie features to strings in plain language users see at install time.
Common rejection reasons
- Remote code execution or obfuscated scripts that hide behaviour
- Permissions wider than the described feature set
- Missing privacy policy when using
storageor identity APIs - Deceptive functionality or unrelated keyword stuffing in metadata
After approval, version bumps go through the same dashboard. Increment version in manifest.json. Use semantic versioning so enterprise users can track change logs.
What tooling speeds up extension development in 2026?
Plain folders work for small extensions. Larger projects benefit from TypeScript, ESLint, and a bundler. Vite 8.x with the CRX plugin can emit a loadable dist/ folder on each save. Keep "type": "module" in manifest when using ES modules in the service worker.
Hot reload is imperfect. Expect to click reload on chrome://extensions after background changes. Content scripts often need a full page refresh on the target site.
For UI-heavy popups, plain Bootstrap 5 markup matches what many Laravel and WordPress teams already ship. Skip React unless the popup is genuinely complex. Bundle size affects review perception and install size alike.
If the extension is one channel in a broader product, plan it during discovery—the same phase as planning and research for software projects. A week of scoping permissions and API contracts saves a month of store resubmissions.
Internal extensions for a company can skip the public store. Policy-install through Google Workspace or distribute the CRX with documented manual steps. Public consumer tools should almost always use the store for auto-updates and trust signals.
Key Takeaways
- Building a Chrome Extension in 2026 means Manifest V3 only: service worker, declared permissions, no remotely hosted logic.
- Keep host permissions narrow; justify every string in the store listing and in your privacy policy.
- Use
chrome.storageand message passing—popups and content scripts are ephemeral by design. - Authenticate through OAuth or server-issued tokens; never ship production secrets inside the package.
- Test unpacked early, zip without dev artefacts, and expect store review to scrutinize broad access.
- Share business logic with your web app via a bundled module to avoid drift between extension and backend.
People Also Ask
Do I need to pay to publish a Chrome extension?
Google charges a one-time Chrome Web Store developer registration fee (USD 5 as of 2026). There is no per-download fee. Budget a few hours for listing assets, privacy policy hosting, and possible resubmission if reviewers flag permissions.
Can Chrome extensions use Manifest V2 in 2026?
No for new public listings. Manifest V2 is deprecated and removed from the Chrome Web Store. Enterprise policies may temporarily sideload older builds, but all new development should target Manifest V3 APIs documented on MDN Web Extensions.
What is the difference between a content script and a service worker?
A content script shares the DOM of a matched page but runs in an isolated JavaScript world. A service worker handles events, network calls, and storage without DOM access. They communicate through chrome.runtime messaging APIs.
How long does Chrome Web Store review take?
Many submissions resolve within one to three business days. Complex permissions, identity scopes, or data-handling declarations can extend review. Fix rejection notes precisely instead of resubmitting unchanged builds.
Ship your extension with a clear scope
Building a Chrome Extension in 2026 rewards small scope and strict permissions more than feature sprawl. Start with one hostname, one user flow, and a service worker that does one job well. Wire it to a backend you already trust, test unpacked until messaging and auth are boring, then publish with honest copy.
If you want an extension paired with a Laravel API, eCommerce workflow, or internal automation tool, see custom software development services or browse the directory platform portfolio work for examples of multi-surface products. For AI-assisted workflows inside the browser, AI integration and automation may fit your roadmap. Ready to scope a build? Contact us with your target site, permissions list, and API docs.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

