Chrome Extension Development Tips — What I Learned Publishing 11 Extensions
I built and submitted 11 Chrome extensions to the Chrome Web Store. Two are live, one is in review, and eight are pending. Here's everything I wish I'd known before starting.
The 11 Extensions
| Extension | Category | Status |
|---|---|---|
| Tab Manager Pro | Productivity | Published |
| Quick Notes | Productivity | Published |
| Hash & Encode Tool | Developer Tools | In Review |
| Color Picker Pro | Developer Tools | Pending |
| JSON Formatter | Developer Tools | Pending |
| Readability Score | Writing | Pending |
| SEO Meta Checker | SEO | Pending |
| Bookmark Organizer | Productivity | Pending |
| Page Speed Checker | Developer Tools | Pending |
| Dark Mode Toggle | Accessibility | Pending |
| Screenshot Capture | Utility | Pending |
Tip 1: Use Manifest V3 from Day One
Manifest V2 is deprecated. Google won't accept new V2 extensions. Don't waste time learning V2 patterns.
Key V3 changes:
-
background.js→ Service Workers (no persistent background page) -
chrome.browserAction→chrome.action - Remote code execution is blocked (no
eval(), no remote scripts) -
content_security_policyis stricter
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0.0",
"action": {
"default_popup": "popup.html",
"default_icon": "icons/icon-48.png"
},
"permissions": ["storage", "activeTab"],
"background": {
"service_worker": "background.js"
}
}
Tip 2: Request Minimum Permissions
Chrome Web Store reviewers scrutinize permissions. Request only what you need:
| Permission | Use When |
|---|---|
activeTab |
Need access to current tab only (preferred over tabs) |
storage |
Saving user preferences |
tabs |
Need to list/manage all tabs |
clipboardWrite |
Copy-to-clipboard feature |
host_permissions |
API calls to specific domains |
Never request: <all_urls> unless absolutely necessary. It triggers manual review and scares users.
Tip 3: The Popup Architecture
Every popup follows this pattern:
popup/
├── popup.html ← Minimal HTML shell
├── popup.css ← Styling (inline if small)
└── popup.js ← All logic here
Keep popups fast. Users expect instant response:
// popup.js
document.addEventListener('DOMContentLoaded', async () => {
// Load saved state
const { settings } = await chrome.storage.local.get('settings');
// Initialize UI
renderUI(settings || defaultSettings);
// Bind events
document.getElementById('save-btn')
.addEventListener('click', handleSave);
});
async function handleSave() {
const settings = collectFormData();
await chrome.storage.local.set({ settings });
showToast('Saved!');
}
Tip 4: Service Workers Die — Handle It
V3 Service Workers are not persistent. They shut down after ~30 seconds of inactivity. This means:
- No global variables that persist between events
-
No
setInterval()for periodic tasks — usechrome.alarms -
Always restore state from
chrome.storageon wake
// background.js (Service Worker)
// BAD: This variable resets when the worker restarts
let counter = 0;
// GOOD: Persist to storage
chrome.runtime.onMessage.addListener(async (msg) => {
const { counter } = await chrome.storage.local.get('counter');
const newCounter = (counter || 0) + 1;
await chrome.storage.local.set({ counter: newCounter });
});
// BAD: setInterval dies with the worker
setInterval(checkSomething, 60000);
// GOOD: Alarms survive worker restarts
chrome.alarms.create('check', { periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'check') checkSomething();
});
Tip 5: Privacy Policy is Required
Chrome Web Store requires a privacy policy URL for any extension that collects data. Even if you don't collect anything, you need one that says so.
I host mine on Vercel as a simple static HTML page:
https://homepage-three-ochre.vercel.app/privacy-policy-[extension-name].html
Template:
<h1>Privacy Policy - [Extension Name]</h1>
<p>This extension does not collect, store, or transmit any personal data.</p>
<p>All data is stored locally on your device using Chrome's storage API.</p>
<p>No analytics, no tracking, no third-party services.</p>
<p>Contact: your-email@example.com</p>
Tip 6: Freemium Model for Chrome Extensions
Free extensions get downloads. But how do you monetize?
My approach — freemium with feature gating:
- Core functionality is free forever
- Premium features (themes, export, bulk operations) require a one-time payment
- Payment verification via a simple license key API
I built a license key validation API on Cloudflare Workers that handles this for all my extensions.
Tip 7: Review Times and Gotchas
Current review timeline (as of March 2026):
- New submissions: 3-7 business days
- Updates to existing: 1-3 business days
- Rejections: Usually permissions-related
Common rejection reasons:
- Requesting unnecessary permissions
- Missing privacy policy
- Misleading description
- Code obfuscation (not allowed in V3)
Publishing Checklist
Before submitting to Chrome Web Store:
- [ ] Manifest V3 with minimum permissions
- [ ] Icons: 16x16, 48x48, 128x128 PNG
- [ ] Screenshots: 1280x800 or 640x400 (at least 1)
- [ ] Privacy policy URL (hosted and accessible)
- [ ] Description: clear, no keyword stuffing
- [ ] Test on Chrome stable, beta, and dev channels
- [ ] No console.log() in production code
- [ ] Content Security Policy configured
Resources
My extensions: Search "miccho27" on Chrome Web Store (or check back as more get approved)
Other developer tools I've built:
Building in public from Paraguay. Follow for more on Chrome extensions, APIs, and indie development.
Top comments (0)