How to Build Chrome Extensions with Claude Without Coding Experience
I wanted to build a web app. I asked Claude to help me code one. Then I hit a wall: hosting costs, domain setup, database configuration, deployment pipelines. I just wanted something simple that worked on my computer. That’s when someone on Reddit mentioned Chrome extensions.
You might also enjoy making Chrome extensions, if there’s a little something your browser doesn’t do but wish it could, you might be able to get Claude to make an extension. No need to make public, can run it just on your own computer.
This changed everything. Chrome extensions are the perfect entry point for non-coders who want to build something useful with AI.
Why Chrome Extensions Work for Beginners
Full web apps require:
- A hosting service (Vercel, Netlify, AWS)
- A domain name
- Backend infrastructure
- Database setup
- Deployment pipelines
- Ongoing costs
Chrome extensions require:
- A folder on your computer
- Chrome browser
That’s it. No servers. No hosting. No monthly fees. You load the extension locally, it runs in your browser, and you control everything.
What Claude Can Generate for You
A Chrome extension has a predictable structure. Claude knows this structure and can generate all the pieces:
manifest.json - The configuration file that tells Chrome what your extension does
content.js - Scripts that run on web pages you visit
popup.html - The little window that appears when you click your extension icon
background.js - Scripts that run in the background (optional)
You describe what you want, and Claude produces these files.
My First Extension: A Simple Example
I wanted an extension that highlights specific words on any webpage. I told Claude:
I want a Chrome extension that highlights the word “deadline” in red on every webpage I visit.
Claude generated this manifest.json:
{ "manifest_version": 3, "name": "Deadline Highlighter", "version": "1.0", "description": "Highlights the word 'deadline' in red on all webpages", "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"] } ]}And this content script:
// Find and highlight all instances of "deadline"function highlightDeadlines() { const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, null, false );
const textNodes = []; while (walker.nextNode()) { if (walker.currentNode.textContent.toLowerCase().includes('deadline')) { textNodes.push(walker.currentNode); } }
textNodes.forEach(node => { const span = document.createElement('span'); span.innerHTML = node.textContent.replace( /deadline/gi, '<mark style="background-color: red; color: white; padding: 2px;">$&</mark>' ); node.parentNode.replaceChild(span, node); });}
// Run when page loadshighlightDeadlines();
// Watch for dynamic contentconst observer = new MutationObserver(highlightDeadlines);observer.observe(document.body, { childList: true, subtree: true });I saved both files in a folder called deadline-highlighter. That was the entire codebase. Two files.
How to Load Your Extension Locally
This part confused me at first. I expected some complex deployment process. Here’s what I actually did:
1. Open Chrome and go to chrome://extensions2. Enable "Developer mode" toggle in the top right3. Click "Load unpacked" button4. Select your extension folder (the one with manifest.json)5. That's it. Your extension icon appears in the toolbar.No app store. No review process. No publishing. The extension runs on your machine only.
What Went Wrong (And How I Fixed It)
My extension worked, but I noticed a problem. On some sites, the highlights flickered or appeared multiple times. The mutation observer was running too aggressively.
I went back to Claude:
The highlights are flickering on dynamic sites like Twitter. Can you fix this?
Claude explained that the observer was triggering on its own DOM changes. The fix:
function highlightDeadlines() { // Prevent infinite loops by checking if we already highlighted const existing = document.querySelectorAll('mark[data-deadline-highlight]'); existing.forEach(el => { const text = document.createTextNode(el.textContent); el.parentNode.replaceChild(text, el); });
const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, null, false );
const textNodes = []; while (walker.nextNode()) { if (walker.currentNode.textContent.toLowerCase().includes('deadline')) { textNodes.push(walker.currentNode); } }
textNodes.forEach(node => { const span = document.createElement('span'); span.innerHTML = node.textContent.replace( /deadline/gi, '<mark data-deadline-highlight style="background-color: red; color: white; padding: 2px;">$&</mark>' ); node.parentNode.replaceChild(span, node); });}
// Debounce the observerlet timeout;const observer = new MutationObserver(() => { clearTimeout(timeout); timeout = setTimeout(highlightDeadlines, 100);});observer.observe(document.body, { childList: true, subtree: true });The key changes: added a data attribute to track highlights, debounced the observer, and cleaned up old highlights before re-applying. I didn’t know what “debounce” meant before this. Claude explained it: wait 100ms before running, and if another change happens during that wait, restart the timer. This prevents rapid-fire executions.
Adding a Popup to Control the Extension
I wanted to turn the highlighter on and off without reloading the page. I asked Claude for a popup.
Claude generated popup.html:
<!DOCTYPE html><html><head> <style> body { width: 200px; padding: 10px; font-family: sans-serif; } button { width: 100%; padding: 10px; background: #4285f4; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background: #3367d6; } .status { text-align: center; margin-top: 10px; font-size: 12px; } </style></head><body> <button id="toggle">Toggle Highlights</button> <div class="status" id="status">Highlights: ON</div> <script src="popup.js"></script></body></html>And popup.js:
document.getElementById('toggle').addEventListener('click', async () => { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.sendMessage(tab.id, { action: 'toggle' }, (response) => { document.getElementById('status').textContent = `Highlights: ${response.enabled ? 'ON' : 'OFF'}`; });});I also needed to update manifest.json to add the popup:
{ "manifest_version": 3, "name": "Deadline Highlighter", "version": "1.0", "description": "Highlights the word 'deadline' in red on all webpages", "action": { "default_popup": "popup.html" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"] } ]}The content.js also needed to handle the toggle message:
let enabled = true;
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'toggle') { enabled = !enabled; if (enabled) { highlightDeadlines(); } else { removeHighlights(); } sendResponse({ enabled: enabled }); }});
function removeHighlights() { const highlights = document.querySelectorAll('mark[data-deadline-highlight]'); highlights.forEach(el => { const text = document.createTextNode(el.textContent); el.parentNode.replaceChild(text, el); });}After these changes, I clicked the extension icon and saw a popup with a toggle button. The whole thing took about 20 minutes of back-and-forth with Claude.
The Key Insight: Smaller Scope, Faster Wins
Building a web app would have taken me days. I would have learned about hosting, databases, authentication, and deployment. Those are valuable skills, but they’re not the skills I wanted to practice right now.
The Chrome extension taught me:
- How to structure a project
- How to read and modify code
- How to debug (the Chrome DevTools extension page shows console.log output from content scripts)
- How to iterate on a design
And I ended up with something I use daily. Every time I open a webpage, I see deadlines highlighted. It’s a small win that motivates me to build more.
Common Pitfalls I Encountered
Manifest version issues: Chrome recently moved to Manifest V3. Some tutorials still reference V2. Claude knows the difference and generates V3 by default. If you see errors about deprecated features, mention the version.
Content Security Policy: Some extensions try to load external scripts. Chrome blocks this for security. I asked Claude why my Google Fonts weren’t loading in the popup. The answer: embed the font or use a system font. Local resources only.
Permissions: Extensions need to declare what they can access. If your extension stops working on certain sites, check the matches pattern in manifest.json. <all_urls> works everywhere but requires more permissions. You can limit to specific sites:
{ "content_scripts": [ { "matches": ["https://*.github.com/*", "https://*.stackoverflow.com/*"], "js": ["content.js"] } ]}Hot reload: Unlike a web app with live reload, extensions don’t auto-refresh. After code changes, you need to click the refresh icon on the chrome://extensions page. I forgot this multiple times and thought my code was broken.
Extension Ideas That Work Well
If you’re not sure what to build, these work well for first extensions:
- Text replacer: Change specific words on all pages (useful for inside jokes or avoiding spoilers)
- Link opener: Find all links matching a pattern and open them in new tabs
- Copy formatter: Add buttons to copy page content in a specific format (Markdown, plain text)
- Time zone converter: Highlight or convert time strings on pages
- Keyboard shortcut mapper: Add custom keyboard actions to websites
The pattern is simple: describe something your browser doesn’t do but you wish it could. Claude generates the code. You load it locally and iterate.
When You’re Ready for More
Once your extension works, you can:
- Add options: Right-click your extension icon and choose “Options” to add a settings page
- Store data: Use
chrome.storageto save user preferences - Add notifications: Show system notifications with
chrome.notifications - Context menus: Add right-click menu items with
chrome.contextMenus
All of these have official documentation, but Claude can generate working examples faster than you can read the docs.
Summary
Chrome extensions are the simplest way to build something useful with AI assistance. No hosting, no deployment, no costs. You describe what you want, Claude generates the files, and you load them in Chrome.
Start with a small problem your browser doesn’t solve. Build the extension locally. Iterate with Claude when things break or need features. You’ll learn coding concepts without the overhead of full web development.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments