How to Build a Chrome Extension with Claude Code
Learn step-by-step how to create a Chrome extension using Claude Code with practical tips and common troubleshooting advice.

Building a Chrome extension with Claude Code starts with understanding the architecture, not the code. Manifest V3 is unintuitive: background service workers do not persist, content scripts run in isolated contexts, and the popup reloads on every open.
Claude Code generates the correct Manifest V3 structure from the first file when you describe what the extension does. This guide covers every component: the manifest, service worker, content script, popup, testing, and where Claude Code needs extra direction.
Key Takeaways
- Manifest V3 is required: Chrome deprecated Manifest V2 in 2024. All new extensions must use V3, which replaces background pages with service workers and enforces stricter CSP rules.
- Four core files start every extension:
manifest.json,background.js(service worker),content.js(content script), andpopup.htmlwithpopup.js. - Service workers do not persist: State stored as JavaScript variables disappears when the service worker sleeps. Use
chrome.storage, not in-memory variables. - Content scripts are isolated: Content scripts cannot access the page's JavaScript variables. They communicate with the background via
chrome.runtime.sendMessage. - Testing is manual or Puppeteer-based: Load the unpacked extension in
chrome://extensionsand reload after every change. There is no automated test runner for extension UIs. - Store submission costs $5: Chrome Web Store charges a one-time developer registration fee. Claude Code can generate the required store assets list and description.
What Is the Manifest V3 Architecture and Why Does It Matter?
Manifest V3 replaces persistent background pages with event-driven service workers, requires explicit permission declarations, and enforces a messaging system for cross-context communication. Understanding this before the first prompt avoids the most common build failures.
Every Chrome extension is four coordinated components. Claude Code generates all four when instructed. Knowing what each component does determines how you prompt for each one.
- The manifest file:
manifest.jsondeclares the extension's permissions, files, and metadata. Chrome blocks any API call not declared here, silently, without a useful error. - Background service worker: Handles events, coordinates across tabs, and manages state. It is event-driven, not persistent, meaning it sleeps when idle.
- Content script: Runs in the context of web pages. It can read and modify the DOM but cannot access the page's JavaScript variables or framework state.
- Popup: A standard HTML page that opens when the user clicks the extension icon. It reloads completely every time it opens.
- The messaging system: Content scripts and the background service worker communicate via
chrome.runtime.sendMessageandchrome.runtime.onMessage. This is the pattern Claude Code generates for any cross-component feature. - When you need fewer components: A popup-only extension does not need a content script. A background-only extension does not need a popup. Specify only what your extension actually needs.
The permissions model is the most common source of silent failures. Every Chrome API your extension uses must be declared in manifest.json before Chrome will allow the call.
How Do You Set Up the Extension Project?
Create CLAUDE.md before writing the first prompt. A single sentence describing what the extension does, plus a list of Chrome APIs it will use, gives Claude Code enough context to generate correct Manifest V3 code from the start.
Start with the file structure, then the manifest, then each component individually. Generating all files at once without a reviewed manifest produces harder-to-debug issues.
- Project structure:
manifest.jsonat the root,src/background.js,src/content.js,src/popup/popup.html,src/popup/popup.js,src/popup/popup.css, andicons/with 16px, 48px, and 128px PNGs. - CLAUDE.md contents: Extension purpose in one sentence, the Manifest V3 requirement, the permissions list, and which components communicate with which.
- First prompt: Ask Claude Code to generate
manifest.jsonwithmanifest_version: 3, the extension name, description, and permissions. Review this before generating any other file. - No build tool required: Claude Code generates plain JavaScript that loads directly in Chrome. Specify TypeScript in CLAUDE.md before starting if you want a Vite + CRXJS setup.
- CLAUDE.md patterns transfer: The full stack build approach with Claude Code covers CLAUDE.md project configuration patterns that apply directly to extension builds.
Reviewing the manifest before any other file is generated saves significant debugging time. The permissions section is easier to correct at the manifest stage than after ten files reference undeclared APIs.
How Do You Build the Background Service Worker?
The background service worker is event-driven. Generate event listener stubs first, then add logic. Any state that must survive the service worker sleeping goes in chrome.storage, not in module-level variables.
Prompt Claude Code to use chrome.storage.local.get and chrome.storage.local.set for all state. This is the single most important pattern to get right in Manifest V3.
- Event listener stubs first: Ask Claude Code to generate listeners for
chrome.runtime.onInstalled,chrome.action.onClicked,chrome.tabs.onUpdated, and message events before adding any logic. - Central message handler: Generate one
chrome.runtime.onMessage.addListenerthat dispatches to action-specific functions based on amessage.typefield. This is cleaner and easier to extend than multiple listeners. - State persistence pattern: Specify "use
chrome.storage.localfor all state" in CLAUDE.md and in every service worker prompt. In-memory variables disappear when the worker sleeps. - Context menus: Register context menu items in the
onInstalledlistener. They persist until removed, soonInstalledis the correct registration point, not a click handler. - Scheduled tasks: If the extension needs periodic logic, specify "use
chrome.alarms" in the prompt.chrome.alarmsis the Manifest V3 replacement forsetTimeoutin background contexts.
If Claude Code generates state as module-level variables in the service worker, correct it immediately. This is the Manifest V3 pattern that causes the most confusing runtime behaviour.
How Do You Build the Content Script?
Content scripts run in an isolated world: they can read and modify the DOM but cannot access the page's JavaScript variables, framework instances, or app state. This is a Chrome architecture constraint, not a Claude Code limitation.
Prompt Claude Code to generate the manifest entry and the content script file together, so the matches pattern, file path, and run_at value are consistent from the start.
- Manifest declaration: Include
matches(which URLs to run on),js(the script file path), andrun_at: "document_idle"in the content script entry inmanifest.json. - DOM-only operations: Content scripts can query selectors, modify DOM elements, and observe mutations. Prompt for DOM-based operations only. Do not ask Claude Code to read page JavaScript state.
- Sending data to the background:
chrome.runtime.sendMessage({ type: 'PAGE_DATA', payload: data })from the content script. Prompt Claude Code to generate both the send call and the receive handler in the same step. - Injecting UI into the page: Content scripts can append DOM elements. Ask Claude Code to use shadow DOM if the injected UI should not be affected by the page's CSS.
- Programmatic injection:
chrome.scripting.executeScriptfrom the background service worker runs code in the page on demand. Use this when the content script should only run on user action, not on every page load.
The content script isolation constraint is often misunderstood as a Claude Code limitation. It is not. Chrome enforces this boundary regardless of how the code is written.
How Do You Build the Popup?
The popup is a standard HTML page. Treat it that way. Every piece of state the popup needs must be loaded from chrome.storage on open, because the popup reloads completely every time the user clicks the extension icon.
Prompt Claude Code to use chrome.storage.local.get inside DOMContentLoaded to restore state. Any variable set in a previous popup session is gone when the popup reopens.
- Basic popup structure:
popup.htmlwith<script src="popup.js">at the bottom. No framework required for simple popups. React works if you use a build tool. - State on load: Prompt Claude Code to read all needed state from
chrome.storage.localinsideDOMContentLoaded. Never rely on variables from a previous session. - Popup-to-background communication:
chrome.runtime.sendMessagefrompopup.jsto the service worker. Generate this pattern early so Claude Code uses it consistently throughout the popup build. - Getting the active tab: Specify "get the currently active tab" in the prompt. Claude Code generates
chrome.tabs.query({ active: true, currentWindow: true })correctly when the intent is stated clearly. - Popup sizing: Set explicit dimensions in
popup.css:body { width: 300px; min-height: 200px; }. Chrome popups have a default minimum width that overrides smaller values.
The popup reload behaviour surprises most first-time extension developers. Treating the popup as a stateless view that loads its data on open, every time, eliminates a full class of bugs.
How Do You Test a Chrome Extension with Claude Code?
Chrome extension testing is primarily manual: load the unpacked extension, reload after every change, and test in the browser. Claude Code can generate a Puppeteer test suite and a manual test checklist to make this process systematic.
The service worker console and the content script console are in two different DevTools locations. Knowing where to look for errors saves significant debugging time.
- Load the unpacked extension: Open
chrome://extensions, enable Developer Mode, click "Load unpacked," and select the extension directory. Reload after every Claude Code output change. - Background service worker errors: Open
chrome://extensionsand click the "Service Worker" link under your extension. This console shows background errors not visible in the normal DevTools. - Content script errors: Open DevTools on any page where the content script runs. Content script
console.logoutput appears in the page's DevTools console, not in the extension console. - Puppeteer tests: Ask Claude Code to write Puppeteer tests using the
--load-extensionflag for basic popup and background logic testing. This is the closest available option to automated extension testing. - Manual test checklist: Ask Claude Code to generate a checklist of the extension's key behaviours, covering every permission flow and UI interaction. Manual checklists are more reliable than automated tests for permission-based flows.
- Terminal-based testing patterns: Projects like building a Slack bot with Claude Code and building a Telegram bot with Claude Code involve similar test-in-terminal workflows when UI automation is limited.
Systematic manual testing with a generated checklist is faster than trying to automate Chrome's permission UI flows. Use automation for logic tests and manual testing for browser interaction.
What Does Claude Code Handle Well in Chrome Extension Builds?
Claude Code generates correct Manifest V3 structure, chrome.storage patterns, and message passing boilerplate reliably. The two areas that need verification after every generation run are the permissions list and any code that uses eval or loads remote scripts.
Knowing where Claude Code excels and where it needs review lets you direct the build more effectively and catch issues before they cause silent failures in Chrome.
- Manifest generation: Claude Code generates correct
manifest.jsonstructure for Manifest V3 when "Manifest V3" is specified explicitly in CLAUDE.md and in the prompt. - Storage and messaging boilerplate:
chrome.storageread/write patterns andchrome.runtime.sendMessagehandler patterns are generated cleanly and consistently. - Content script DOM manipulation:
querySelector,insertAdjacentHTML, andMutationObserverpatterns work well when the target DOM structure is described in the prompt. - Permissions gaps: Claude Code may generate code that calls a Chrome API without adding the corresponding
manifest.jsonpermission. Always verify the permissions section after generating new feature code. - MV2 pattern contamination: Claude Code's training data includes Manifest V2 patterns that are no longer valid. If generated code uses
evalor loads remote scripts, it needs correction before loading in Chrome. - Non-technical builders: For developers who want to build browser tools without deep JavaScript expertise, building browser tools without writing code covers the Claude Code approach for non-technical extension builders.
The permissions verification step is non-negotiable. Run it after every feature addition, not just at the end of the build.
Conclusion
Chrome extensions built with Claude Code come out cleanest when the Manifest V3 architecture is understood before the first prompt.
The service worker persistence model, the content script isolation constraint, and the permissions requirement are not details to sort out during the build. Knowing them before you start means every prompt Claude Code receives produces code that loads and runs correctly in Chrome.
Before writing the first prompt, add two things to CLAUDE.md: what the extension does in one sentence, and the list of Chrome APIs it will use. That list drives the permissions section and tells Claude Code which APIs to use throughout the build.
Want Your Chrome Extension Built by a Team That Knows the Architecture?
Manifest V3 has specific constraints that catch most first-time extension developers off guard. Service worker state, content script isolation, and permission declarations are all places where incorrect patterns produce silent failures that take hours to diagnose.
At LowCode Agency, we are a strategic product team, not a dev shop. We handle the Manifest V3 architecture, content script and service worker logic, popup state management, and Chrome Web Store submission preparation so your extension works correctly from the first load.
- Architecture scoping: We map your extension's required components, permissions, and cross-component communication before writing a line of code.
- Manifest V3 setup: We generate and validate the
manifest.json, permissions list, and CLAUDE.md configuration for your specific extension requirements. - Service worker build: We implement the event-driven background logic and
chrome.storagestate patterns for persistent, reliable background behaviour. - Content script development: We build DOM interaction and message passing patterns that respect content script isolation and work correctly across target pages.
- Popup UI build: We create the popup interface with correct state loading on open, background communication, and responsive sizing.
- Testing and QA: We build the Puppeteer test suite, manual test checklist, and reload workflow so every Claude Code output change is verified before going live.
- Store submission prep: We generate the Chrome Web Store listing description, screenshot dimensions list, and version update workflow for smooth publication.
We have built 350+ products for clients including Coca-Cola, American Express, and Medtronic.
If you want a working Chrome extension without the architecture research, discuss your Chrome extension project with our team.
Last updated on
April 10, 2026
.









