Skip to content

How to Publish a Chrome Extension to the Web Store in 2026 (Full Checklist)

Chrome extensionsMay 19, 2026

The Web Store review is a product gate — privacy policy, assets, and MV3 compliance matter as much as your code.

How to Publish a Chrome Extension to the Web Store in 2026 (Full Checklist)

You've built the extension. It works locally. Now comes the part nobody enjoys: packaging, listing, and surviving Google's review process.

This post is the full checklist — everything you need from "Load Unpacked works" to "Published on the Chrome Web Store."

Step 0: Developer Account

  1. Go to Chrome Web Store Developer Dashboard
  2. Sign in with a Google account
  3. Pay the $5 one-time registration fee
  4. Accept the developer agreement

That's it. The $5 is non-refundable and per-account, not per-extension. One account can publish unlimited extensions.

Step 1: MV3 Compliance Check

The Web Store only accepts Manifest V3 extensions. Before packaging, verify:

  • "manifest_version": 3 in manifest.json
  • Background script is a service_worker, not a background.page
  • No eval(), new Function(), or inline scripts in extension pages
  • No remotely hosted code (<script src="https://...">)
  • Using chrome.scripting.executeScript instead of chrome.tabs.executeScript
  • Using chrome.declarativeNetRequest if modifying network requests (not chrome.webRequest blocking mode)
  • Content Security Policy doesn't include unsafe-eval (only wasm-unsafe-eval is allowed)

If you followed the TypeScript setup guide, you're already MV3-compliant. If migrating from MV2, Google has a migration checklist.

Step 2: Package the Extension

Build your production bundle:

npm run build

With CRXJS, this outputs to dist/. The folder contains:

  • Compiled JavaScript files
  • HTML pages (popup, options)
  • manifest.json (processed by CRXJS)
  • Icons and assets

Create a ZIP file of the dist folder contents (not the folder itself):

cd dist
zip -r ../my-extension.zip .

Step 3: Prepare Store Listing Assets

You need these before uploading:

Required

  • Extension name — 45 characters max. Include the primary keyword naturally.
  • Summary — 132 characters. Shown in search results. Make it count.
  • Description — Up to 16,000 characters. First 2-3 sentences are most important (shown in preview).
  • Icon — 128×128px PNG. Must look good at 48px and 16px too (Chrome resizes it).
  • Screenshots — At minimum 1, up to 5. Size: 1280×800 or 640×400. Show the actual extension UI, not marketing graphics.
  • Category — Choose the most relevant. "Productivity" is the most competitive.
  • Language — Primary language of the extension.

Required if Accessing User Data

  • Privacy policy URL — Required for any extension that uses activeTab, storage, host permissions, or collects any data. Must be publicly accessible.
  • Permissions justification — For each permission declared, explain WHY the extension needs it. This is filled in during the submission form. Be specific.

Optional but Recommended

  • Promo tile — 440×280 PNG. Used for featured placement.
  • YouTube video — Embedded in the listing. Dramatically increases installs.
  • Website URL — Link to your site for credibility.

Step 4: Write a Privacy Policy

Every extension that accesses user data needs one. Here's the minimum viable privacy policy structure:

  1. What data you collect — Be specific. "Browsing history on blocked sites" not "some data."
  2. How you use it — "To display time tracking statistics locally."
  3. Where it's stored — "All data is stored locally using chrome.storage. No data is transmitted to external servers."
  4. Third-party sharing — "We do not share any user data with third parties."
  5. Contact — An email address for privacy inquiries.

If your extension is fully local (like ResistGate or Amethyst), the policy is short: "All data stays on your device. Nothing is sent anywhere."

Host this on your website. A simple page at yoursite.com/privacy or yoursite.com/extension/privacy-policy works. Google checks that the URL is accessible.

Step 5: Upload and Submit

  1. Go to the Developer Dashboard
  2. Click "New Item"
  3. Upload the ZIP file
  4. Fill in all listing fields
  5. Upload screenshots and icons
  6. Add privacy policy URL
  7. Justify each permission in the designated fields
  8. Choose distribution: Public (everyone), Unlisted (link only), or Private (specific users)
  9. Click "Submit for Review"

Step 6: The Review Process

Timeline

  • Initial review: 1-3 business days for simple extensions
  • Extensions with broad permissions: 3-7+ business days
  • Extensions with <all_urls>: Expect longer review. Detailed justification required.
  • Updates to existing extensions: Usually faster than initial review (1-2 days)

What Reviewers Check

  1. Permission justification — Every requested permission must have a clear reason
  2. Code clarity — Obfuscated or heavily minified code gets flagged
  3. Privacy policy accuracy — Must match actual data practices
  4. MV3 compliance — All the items from Step 1
  5. Malware/spam — Automated scans run first, then human review if flagged
  6. Store policies — No deceptive functionality, no unauthorized data collection

Common Rejection Reasons (and Fixes)

"This extension requests more permissions than necessary"

Cause: Using <all_urls> when narrower host permissions would work, or requesting tabs when activeTab suffices.

Fix: Audit your permissions. Move non-essential permissions to optional_permissions. In the justification, explain exactly why each permission is needed for core functionality.

"Privacy policy not found or insufficient"

Cause: Broken privacy policy URL, or policy doesn't mention the specific data the extension accesses.

Fix: Verify the URL works. Mention each permission's data access explicitly. For extensions using storage, state where data is stored (locally vs. synced).

"Code readability"

Cause: Heavy minification or bundler output that looks obfuscated.

Fix: Configure Vite to produce readable output in production:

// vite.config.ts
export default defineConfig({
  build: {
    minify: false, // or 'terser' with readable options
    sourcemap: true,
  },
  // ...
});

Source maps help reviewers but aren't strictly required. Readable output without sourcemaps usually passes.

"Deceptive functionality"

Cause: Extension does something not described in the listing, or the description implies functionality that doesn't exist.

Fix: Match listing description to actual features exactly. If your extension blocks sites, say it blocks sites. Don't claim it "boosts productivity by 300%" without evidence.

"Duplicate or spam"

Cause: Listing is too similar to an existing extension, or multiple extensions from the same account do nearly the same thing.

Fix: Differentiate clearly. Explain what makes your extension different from alternatives.

Post-Publication Checklist

After approval:

  • Test the installed version from the Web Store (not just the dev version)
  • Verify the listing appears in search for your target keyword
  • Check all screenshots and description render correctly
  • Add the Web Store link to your website
  • Submit to extension directories and review sites for visibility

Updating Your Extension

  1. Increment the version in manifest.json
  2. Rebuild and re-zip
  3. Go to Developer Dashboard → your extension → "Package" tab
  4. Upload the new ZIP
  5. Submit for review

Updates go through the same review process but are typically faster (1-2 days).

Breaking change warning: If you add new permissions in an update, Chrome will disable the extension for existing users until they accept the new permissions. Minimize permission changes in updates.

Pricing Models

The Web Store supports free, freemium (free + in-app purchase), and paid extensions. Options:

Free — Maximum installs, build audience. Monetize through a separate product or service.

Freemium — Core features free, premium features behind a payment. Use chrome.storage.sync to store license status. Implement license checking via your own backend or a service like Gumroad/LemonSqueezy.

Paid — Lowest install rate but direct revenue. Chrome Web Store Payments is deprecated — use external payment processing.

ResistGate uses freemium: core blocking is free, analytics and commitment mode are paid.

The Full Checklist (Copy This)

PRE-SUBMISSION
[ ] manifest_version: 3
[ ] All code bundled locally (no remote scripts)
[ ] No eval() or unsafe-eval in CSP
[ ] Service worker handles idle termination correctly
[ ] Permissions audited — minimum necessary set
[ ] Privacy policy hosted and accessible
[ ] Icons: 128×128, 48×48, 16×16
[ ] Screenshots: 1280×800, showing real UI
[ ] Description written with target keywords
[ ] Tested in Chrome, Edge, Brave

SUBMISSION
[ ] ZIP contains dist/ contents (not the folder)
[ ] All listing fields filled
[ ] Permission justifications written
[ ] Privacy policy URL added
[ ] Distribution set to Public

POST-APPROVAL
[ ] Install from Web Store and test
[ ] Verify search visibility
[ ] Add Web Store link to website
[ ] Monitor reviews for bug reports

What's Next

If you've followed this series from the beginning, you've gone from empty folder to understanding the architecture, typed message passing, persistent storage, React popups, locked-down permissions, AI integration, AI-assisted building, and a real product case study — all the way to published on the Web Store.

Ship something.


This is the final post in the Chrome Extensions from Zero to Product series. If you found it useful, ResistGate and Amethyst are the products this series was born from.

Notes from the build

Get more AI engineering insights

Follow the work: AI tools, browser products, product decisions, and honest lessons from the build.

By subscribing, you agree to receive Orlando's emails. No spam. Unsubscribe anytime.

How to Publish a Chrome Extension to the Web Store in 2026 (Full Checklist) | Orlando Ascanio