To make a Chrome extension, create a dedicated folder and add a manifest.json file specifying Manifest V3. Build your user interface using standard HTML, CSS, and JavaScript. If your extension needs to read or modify web pages, add a content script. For background events, register a service worker. Finally, test your build locally by loading it unpacked in chrome://extensions before zipping the final directory to publish on the Chrome Web Store.
You want to build a Chrome extension. The technical barrier is low—if you know basic HTML, CSS, and JavaScript, you already have the required skills. The real challenge is understanding the strict, isolated architecture that Chrome enforces.
This Chrome extension tutorial walks you through building and publishing a working Page Analyzer tool.
Project Overview:
- Time estimate: 45 minutes
- Prerequisites: Basic web development knowledge
Market Context: There are over 262,566 Chrome extensions available. Distribution is hard; building the MVP is fast.
Warning: Avoid Outdated Code
If a tutorial usesmanifest_version: 2orbackground.scripts, close it immediately. Manifest V2 was disabled everywhere on July 24, 2025. Manifest V3 strictly prohibits remotely hosted code and enforces event-driven background logic.
How Chrome Extensions Work: The "Three Worlds" Mental Model
A Chrome extension operates as a group of isolated execution contexts. The popup provides temporary UI, the content script interacts directly with the active webpage's DOM, and the service worker handles background browser events. These isolated worlds do not share normal JavaScript variables and must communicate via message passing.
- Popup: Temporary visual interface.
- Content script: Reads and modifies the active page.
- Service worker: Invisible, event-driven background logic.
Extensions differ from standard web apps because they rely on permission-gated APIs and run components in completely isolated lifecycles.
Data Flow in This Tutorial
- The user clicks Analyze in the popup.
- The popup sends a message to the content script.
- The content script reads the active webpage's DOM.
- The response routes back to the popup.
- The popup renders the analytical results.
- The extension saves the result in
chrome.storage.
Set Up the Project and the First Manifest
The only strictly required file is manifest.json, placed in the root folder. A practical Manifest V3 extension also includes HTML/JS/CSS for the popup, a content script to access page data, a service worker for background tasks, and icon assets for the Chrome Web Store.
Create the Folder Structure
Set up a clean directory containing:
manifest.json(Configuration)popup.html,popup.js,popup.css(Interface bundle)content.js(Page DOM reader)service-worker.js(Background logic)icons/(Image assets)
Write the Minimal Manifest V3
Start small. Invalid JSON formatting causes most upload errors. Your manifest.json must contain zero comments.
{
"manifest_version": 3,
"name": "Page Analyzer",
"version": "1.0",
"description": "Extracts word count, links, and headings from the active tab.",
"action": {
"default_popup": "popup.html",
"default_title": "Analyze Page"
}
}Choose Permissions Carefully
Define API access using the "permissions" array.
Request activeTab instead of <all_urls> whenever possible. It grants temporary execution rights on the current page only after a user gesture (like a click), avoiding invasive install warnings.
Critical MV3 Constraints:
- No remote code. Bundle every script inside the extension package.
- Strict CSP. Inline JavaScript is blocked. Move all logic to separate
.jsfiles.
Build the Popup UI
A Chrome extension popup is a lightweight HTML page that opens when a user clicks the extension action icon. It handles quick user interactions and status displays. Because popups close and clear their memory the moment a user clicks away, they should never store long-term state.
- Keep the UI task-focused.
- The JS context dies immediately when the popup closes.
Create popup.html
Include a title, a trigger button, and a results container.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<h3>Page Analyzer</h3>
<button id="analyzeBtn">Analyze Current Page</button>
<div id="results"></div>
<script src="popup.js"></script>
</body>
</html>Create popup.js
Wire the button to identify the active tab.
document.getElementById('analyzeBtn').addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
// Messaging logic triggers here next
});The Popup Trap: When the popup closes, all local variables vanish. Persist necessary data externally.
Add the Content Script to Read the Page
A content script in a Chrome extension is JavaScript injected into a web page to read or modify its DOM. It lives in an "isolated world," meaning it shares the DOM with the host page but maintains its own separate JavaScript environment, preventing variable conflicts.
Register content.js
Inject the script via the manifest.
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]Note: Scope this to specific domains in production to improve security.
Extract Page Data
Query the DOM inside content.js.
function analyzePage() {
return {
title: document.title,
wordCount: document.body.innerText.split(/\s+/).length,
linkCount: document.querySelectorAll('a').length,
headingCount: document.querySelectorAll('h1, h2, h3').length
};
}Content Script Capabilities:
- Can: Read and manipulate the DOM. Listen to extension messages.
- Cannot: Access variables initialized by the webpage's own JavaScript. Call most Chrome APIs directly.
Add the Service Worker for Background Logic
Manifest V3 service workers are ephemeral and event-driven. Global variables are wiped during shutdown. To preserve data, use chrome.storage, and avoid relying on long-running background processes.
Register the Worker
"background": {
"service_worker": "service-worker.js"
}Handle Browser Events
Listen for high-level events like installations.
chrome.runtime.onInstalled.addListener(() => {
console.log("Page Analyzer installed successfully.");
});Service Worker Survival Rules:
- The Idle Timer: API calls or event triggers reset the 30-second shutdown timer.
- No DOM Access: Service workers cannot access
documentorwindow. - The Debugging Paradox: Leaving Chrome DevTools open forces the worker to stay awake. Always re-test your logic with DevTools closed to simulate real user conditions.
Connect Contexts with Message Passing
Extensions use a message passing API to bridge isolated contexts. Use chrome.tabs.sendMessage() to send data from a popup to a content script, and chrome.runtime.sendMessage() to talk to the service worker.
Build the Request-Response Flow
Send a one-time message from popup.js to the active tab:
document.getElementById('analyzeBtn').addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.sendMessage(tab.id, { action: "ANALYZE" }, (response) => {
if (chrome.runtime.lastError) return;
document.getElementById('results').innerText = `Words: ${response.wordCount}`;
});
});Catch the message in content.js and respond:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "ANALYZE") {
sendResponse(analyzePage());
}
});The Async Gotcha: If your content script executes an asynchronous task before sending a response, you must includereturn true;at the end of the listener. Omitting it instantly closes the channel, returningundefined.
Persist Settings with chrome.storage
Store persistent data using Chrome Extension Storage API via chrome.storage.local. Because service workers terminate unexpectedly and popups destroy variables upon closing, standard web storage APIs like localStorage are insufficient or entirely unavailable in extensions.
Save the Last Analysis
Add the "storage" permission to manifest.json, then cache results in the popup:
// Save data
chrome.storage.local.set({ lastWordCount: response.wordCount });
// Retrieve data
chrome.storage.local.get(['lastWordCount'], (result) => {
if (result.lastWordCount) {
document.getElementById('results').innerText = `Words: ${result.lastWordCount}`;
}
});Download the Chrome Extension Template
If you want to skip the boilerplate, review this basic Chrome extension template.
Final Annotated manifest.json
{
"manifest_version": 3,
"name": "Page Analyzer",
"version": "1.0",
"description": "Extracts page metrics.",
"action": { "default_popup": "popup.html" },
"background": { "service_worker": "service-worker.js" },
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"permissions": ["activeTab", "storage"]
}Load, Test, and Debug Locally
Navigate to chrome://extensions, enable Developer mode, and click Load unpacked to select your project folder. Click the extension card's reload icon after editing background scripts or the manifest.
Debugging Workflows:
- Popup Issues: Right-click inside the open popup and select Inspect to launch a dedicated DevTools window.
- Service Worker Errors: Click Inspect views: service worker on the extension card.
- Manifest Failures: If Chrome detects syntax errors, an Errors button appears on the card. Check this first.
Publish Your Chrome Extension to the Chrome Web Store
Register a Chrome Web Store developer account and pay the $5 one-time registration fee. Zip your files with manifest.json at the root. Upload the ZIP in the developer dashboard, fill out the Store Listing and Privacy tabs, and submit for review.
First-Try Approval Checklist
Manual review delays happen when permissions are overly broad or privacy disclosures do not match code behavior.
- Keep One Narrow Purpose: Chrome strictly enforces the single-purpose policy.
- Request Minimum Permissions: Swap
<all_urls>foractiveTabto significantly lower rejection risks. - No Remote Code: Package all required logic inside the local ZIP.
- Sync Disclosures with Reality: If you collect analytics, state it clearly. Provide reviewers with test credentials if your tool requires a login.
Level Up: Frameworks, AI, and Python Backends
Yes, you can create a Chrome extension with React using a bundler to output standard MV3 files. Python, however, cannot run as the browser frontend. To use Python, build a backend API and have your extension's JavaScript communicate with it.
How to Create a Chrome Extension with React
Learn raw MV3 before adding abstractions. Switch to React or specialized build tools like WXT only when your UI state becomes too complex for vanilla JavaScript. React handles the UI layer, but you still ship standard background and content scripts.
How to Make a Chrome Extension with AI
AI accelerates boilerplate generation, but do not blindly trust it. LLMs frequently hallucinate invalid permissions or output deprecated Manifest V2 syntax. Manually verify the generated manifest.json against official Chrome documentation.
How to Create a Chrome Extension in Python
To leverage Python, deploy a FastAPI or Flask application to a server. Your extension's service worker can then send standard HTTP fetch() requests to that Python backend to process heavy server-side workloads.
Post-Launch Monetization
Once you reach consistent active users, evaluate monetization models like freemium limits, paid unlocks, or consent-based support.
If you want to keep the extension free without displaying ads, consider opt-in bandwidth-sharing platforms like Mellowtel. Users explicitly consent to share unused internet bandwidth. Because Chrome reviews are strict, ensure any monetization integration complies with user privacy guidelines and offers a clear, non-dismissible opt-in flow on installation.
FAQ
How hard is it to make a Chrome extension?
Creating a basic Chrome extension is easy for anyone familiar with web development. The difficulty lies entirely in navigating the isolated execution environments and adhering to Chrome's strict Manifest V3 security policies.
How much does it cost to make a Chrome extension?
Developing and testing a Chrome extension locally is free. Publishing requires a one-time $5 developer account registration fee paid to Google.
Should I use a Chrome extension maker?
Use a no-code Chrome extension maker to validate an idea rapidly. For long-term maintainability, custom content scripts, and deep API access, code it yourself. Builders abstract away code but cannot bypass Chrome Web Store policies.
Can I make a Chrome extension in JavaScript?
Yes, JavaScript is the native language for Chrome extensions. Content scripts, service workers, and popup logic must all be written in JavaScript (or compiled to it from TypeScript or React).
Conclusion
You now know how to make a Chrome extension from scratch. You understand the "Three Worlds" architecture, how to extract DOM data safely, and how to persist state across an ephemeral service worker.
Build your first version using raw files. Enforce strict permission discipline, test with DevTools closed, and respect the single-purpose rule for a smooth Chrome Web Store review.