Chrome Extension Extension Packaging — Developer Guide

4 min read

Extension Packaging Guide

This guide covers how to package your Chrome extension for distribution—either through the Chrome Web Store or for self-hosting. You’ll learn what to include, what to exclude, and how to automate the packaging process in your CI/CD pipeline.


Overview

Packaging your extension is the final step before distribution. The package format depends on your distribution method:

This guide covers all three approaches plus automation strategies.


Directory Structure for Packaging

Your extension needs a clean, production-ready directory before packaging. A typical structure looks like:

dist/
├── manifest.json
├── background.js
├── popup.html
├── popup.js
├── content.js
├── styles.css
└── icons/
    ├── icon-16.png
    ├── icon-32.png
    ├── icon-48.png
    └── icon-128.png

This dist folder should contain only the files needed at runtime—no source files, build artifacts, or development tools.


What to Include

Every extension package must include:

File/Directory Required Notes
manifest.json Yes Must be valid Manifest V3
Background script Yes Service worker file
Popup files If applicable HTML, JS, CSS for popup
Content scripts If applicable Injected scripts
Icons Strongly recommended 16, 32, 48, 128px sizes
_locales/ If internationalized Translation files

What to Exclude

Never include the following in your package:


ZIP for Chrome Web Store

The Chrome Web Store accepts extensions as ZIP files. Create your package with:

cd dist
zip -r ../extension.zip . -x ".*" "*.map"

Important Notes


CRX for Self-Hosting

For self-hosted distribution (without the Store), create a CRX file:

Option 1: Using Chrome UI

  1. Open chrome://extensions
  2. Enable Developer mode
  3. Click Pack extension
  4. Select your dist directory
  5. Chrome generates dist.crx and dist.pem

Option 2: Programmatic (crx3 package)

npx crx3 pack ./dist -o extension.crx -p private-key.pem

Important: Private Key


Build Script Example

A typical npm-based build and package workflow:

{
  "scripts": {
    "build": "esbuild src/background.js --bundle --outfile=dist/background.js --minify",
    "prepackage": "npm run build",
    "package": "cd dist && zip -r ../extension.zip . -x '.*' '*.map'",
    "prepublish": "npm run package"
  }
}

More advanced setups might use:


Automated Packaging in CI

Automate your packaging pipeline using GitHub Actions:

name: Build and Package
on: [push, pull_request]

jobs:
  package:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm run build
      - run: npm run package
      - uses: actions/upload-artifact@v4
        with:
          name: extension
          path: extension.zip

Publishing to Chrome Web Store

For automated publishing, use the Chrome Web Store API with a service account:

npx chrome-webstore-upload-cli upload \
  --extension-id=$EXTENSION_ID \
  --client-id=$CLIENT_ID \
  --client-secret=$CLIENT_SECRET \
  --refresh-token=$REFRESH_TOKEN \
  --zip-path=extension.zip

Verification Before Packaging

Always verify your package before distribution:

  1. Run all tests: Ensure nothing is broken
  2. Validate manifest: Use Chrome’s manifest validator
  3. Check file references: Confirm all files in manifest exist in dist
  4. Verify icons: Ensure 16, 32, 48, and 128px icons are correct
  5. Load unpacked: Test one final time with “Load unpacked”

Cross-References

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.