Skip to content
Get the kit

Chrome Web Store

The runbook for your first Chrome Web Store (CWS) submission. Work top to bottom; nothing here assumes a previous publication.

  1. Sign in to the Chrome Web Store Developer Dashboard with the Google account that will own the listing.
  2. Pay the one-time $5 registration fee and verify your email.
  3. Turn on two-factor authentication. A phished publisher account can push a malicious update to every install; the account is a bigger target than the code. Longer term, publish from CI with scoped API credentials, never from laptops.
Terminal window
pnpm zip
pnpm audit:remote-code

pnpm zip builds production and writes the store-ready zip to apps/extension/.output/ (the filename embeds the version). The audit scans the built output for remote-code patterns before review does.

Every permission adds install-warning friction and review time. Pruned modules already took their permissions with them; review two items yourself:

  • The on-page demo matches https://*/*, while your clone keeps it. That broad pattern exists only so the demo status card shows on every page, and it is the kit’s single biggest review-time item. Without on-page UI, remove the demo: commit, then pnpm create extstart --on-page no --yes. With on-page UI, narrow matches to the sites your product operates on, or switch to programmatic injection with activeTab.
  • Every remaining permission needs a one-line justification in the privacy tab:
permissionwhy it’s theredrop it when
storageevery store/state primitivenever (core)
identityGoogle sign-in (launchWebAuthFlow)you remove Google sign-in
offscreenGoogle sign-in fallback (Firebase), used while WXT_GOOGLE_OAUTH_CLIENT_ID is unsetyou set WXT_GOOGLE_OAUTH_CLIENT_ID (the Firebase step does when Google sign-in is on): the build then drops the permission by itself
sidePanelthe sidepanel surfaceyou answer no to the wizard’s Side panel question

Never request <all_urls>, webRequest, cookies, history, or management “for later”: each is a review escalator; add them with the feature that needs them.

Store listing tab: name, description, at least one 1280×800 or 640×400 screenshot, the 128×128 icon (your own, not the kit’s placeholder mug), category, and language. pnpm build && pnpm store-assets:capture && pnpm store-assets captures your popup’s screens over your website and frames them as the screenshots and promo tiles (apps/extension/store-assets/README.md in your clone). Write the description around your single purpose: CWS expects one narrow purpose, and the listed functionality must work on a fresh install (how the kit guarantees that).

Every field must be filled before the submit button enables:

  1. Single purpose: one sentence.
  2. Permission justifications: one per permission; use the table above.
  3. Data usage: paste the kit’s answers from Privacy disclosures.
  4. Certifications: the three compliance checkboxes; Privacy disclosures covers why the unmodified kit satisfies each.
  5. Privacy policy URL: required as soon as you collect any user data (with auth enabled, you do). Host one at the URL in site.config.ts → urls.privacy.
  • Typical review is hours to a few days. Broad host permissions and new developer accounts stretch that, up to a few weeks. Don’t plan a launch on review completing overnight.
  • Deferred publish lets you get approved first and press publish when ready.
  • Staged rollout requires a large existing install base, so it won’t apply to submission #1.
  • If rejected, the email names a code; look it up in Rejection codes.
  • Monitor the listing for unexpected versions.
  • Later releases can go through CI: pnpm submit wraps publish-browser-extension and reads store credentials from the environment (submit:dry validates credentials without uploading). A version tag builds the zips into a GitHub Release; the upload runs when you start “Publish to stores” in the Actions tab with that tag. It publishes to 10% of Chrome users unless the repository variable CHROME_DEPLOY_PERCENTAGE says otherwise. Set it to 100 below 10,000 users: the store allows a partial rollout only above that.
  • Broadcasts reach installs while a review is pending.

Your unpacked builds get their ID from the key in apps/extension/extension-public-key.txt. The store assigns its own ID at the first upload, and pnpm zip leaves the key out because a new store item rejects a manifest that carries one. Google sign-in in the store build needs that ID’s redirect URI too. Either way works:

  • Add a second redirect URI. Copy the item ID from the developer dashboard and add https://<store-id>.chromiumapp.org/ to the OAuth client next to your development one.
  • Switch to the store’s key. In the developer dashboard open the item, then Package → View public key, and paste the whole block into extension-public-key.txt. Rebuild: development now has the store’s ID, so one redirect URI covers both. A draft upload is enough to get the key; you do not have to publish first.

Commit the new key file so teammates and CI follow.