When porting a Chrome extension to Firefox, many developers encounter non-obvious differences: different API namespaces, mandatory AMO signing, and Content Script quirks. Based on our 10+ years of experience and over 50 successful projects, we offer end-to-end Firefox Add-on development that accounts for these nuances. Over 90% of the API matches, but details matter.
Firefox Add-ons are based on WebExtensions API — the same specification as Chrome Extensions. Most Chrome extensions can be ported to Firefox with minimal changes. But there are differences in API support, signing procedure, and extension engine.
Differences Between Firefox and Chrome and Porting Challenges
| Aspect | Firefox | Chrome |
|---|---|---|
| API namespace | browser.* (Promise) + chrome.* |
chrome.* (Callback) |
| Manifest | MV2 and MV3 (MV3 added in Firefox 109+) | Only MV3 (MV2 deprecated) |
| Background | Persistent background page (MV2) or SW (MV3) | Only Service Worker |
| Signing | Mandatory via AMO | Not required |
browser_style |
Supported | Not supported |
Switching from chrome.* to browser.* is the first pitfall. Firefox uses native Promises, while Chrome relies on callbacks. Without webextension-polyfill, code will not work identically in both browsers. Content Scripts in Firefox are isolated via Xray wrapper — direct access to page objects is impossible; you must use window.postMessage. Also, mandatory AMO signing slows down the release cycle if CI is not set up with API keys.
Why Use webextension-polyfill?
The polyfill lets you write code once, using browser.* API with Promises, and it will work in Chrome. This cuts code volume by 3x compared to duplicating for each browser. However, testing is required: some Chrome-specific functions may not have equivalents.
Manifest V2: The Most Stable Option for Firefox
{ "manifest_version": 2, "name": "My Firefox Add-on", "version": "1.0.0", "description": "Description of the add-on", "permissions": [ "storage", "tabs", "activeTab", "https://*.example.com/*" ], "background": { "scripts": ["background.js"], "persistent": false }, "browser_action": { "default_popup": "popup.html", "default_icon": { "48": "icons/icon48.png", "96": "icons/icon96.png" } }, "content_scripts": [{ "matches": ["https://*.target-site.com/*"], "js": ["content.js"] }], "options_ui": { "page": "options.html", "open_in_tab": false } } What Is Xray Wrapper and How to Work With It?
Xray wrapper is an isolation mechanism for Content Scripts in Firefox. It prevents direct access to page JavaScript objects, enhancing security but complicating interaction. For communication between the script and the page, use window.postMessage or CustomEvent. Example:
// Content script -> Page script window.postMessage({ source: 'myExtension', type: 'REQUEST_DATA' }, '*'); // Page script handler window.addEventListener('message', (e) => { if (e.data?.source === 'myExtension' && e.data?.type === 'REQUEST_DATA') { window.postMessage({ source: 'myPage', type: 'RESPONSE_DATA', payload: window.__MY_APP_STATE__, }, '*'); } }); Promise-Based API and Content Script
Firefox supports native Promises in browser.*. The webextension-polyfill makes the code compatible with Chrome:
import browser from 'webextension-polyfill'; // Firefox — native promises const tabs = await browser.tabs.query({ active: true, currentWindow: true }); const tab = tabs[0]; // Execute script (MV2) await browser.tabs.executeScript(tab.id, { code: 'document.body.style.background = "yellow"', }); // Storage await browser.storage.local.set({ key: 'value' }); const result = await browser.storage.local.get('key'); In Firefox, Content Scripts run in an isolated world (Xray wrapper) — they cannot directly access page JavaScript objects. For interaction with the page, window.postMessage or CustomEvent is needed.
Publication, Signing, and CI Testing
Firefox requires signing by Mozilla for any extension not distributed via AMO. Use web-ext — the official Mozilla CLI — and set up signing in CI (e.g., GitHub Actions):
npm install -g web-ext # Development with hot reload web-ext run --source-dir ./dist --firefox-binary "/path/to/firefox" # Build web-ext build --source-dir ./dist --artifacts-dir ./artifacts # Sign (requires AMO API keys) web-ext sign \ --source-dir ./dist \ --api-key $AMO_JWT_ISSUER \ --api-secret $AMO_JWT_SECRET To get API keys: addons.mozilla.org/en-US/developers/addon/api/key/. Without signing, the extension can only be installed via about:debugging:
- Open
about:debugging#/runtime/this-firefox - "Load Temporary Add-on"
- Select
manifest.json - Works until browser restart
For enterprise deployment without AMO — use policies.json in Firefox Enterprise.
Additional: Container Support
Firefox supports Containers — isolated contexts with different cookies. Access via API:const containers = await browser.contextualIdentities.query({}); // { cookieStoreId, name, color, icon } // Open a tab in a specific container await browser.tabs.create({ url: 'https://example.com', cookieStoreId: 'firefox-container-1', }); How We Develop Firefox Add-ons
Our engineers, with 10+ years of experience and over 50 successful projects, follow a standard process: requirements audit, architecture design, implementation using TypeScript and React or vanilla JS, testing across multiple Firefox versions (including Developer Edition and Nightly), automatic signing via CI, and publication on AMO. The result is a fully ready-to-use extension with documentation. By using webextension-polyfill, development is twice as fast compared to separate implementation, and code volume is reduced by 60%.
What's Included in Firefox Add-on Development
| Component | Description |
|---|---|
| Source code | Full extension code (MV2 or MV3) |
| AMO integration | Signing and publication in the store |
| Documentation | Installation and configuration instructions |
| Compatibility guarantee | Support for latest Firefox versions |
| Support | 30 days after delivery |
Timeline and Cost
A Firefox Add-on adapted from a Chrome extension, with signing and AMO publication — 2–3 business days in addition to the ready Chrome extension. Development of a new add-on from scratch — 4–8 days depending on functionality. Cost is calculated individually based on complexity — contact us for an accurate estimate.
Order Firefox Add-on development: get a consultation from our specialists today. We guarantee compatibility with the latest Firefox versions and full documentation. Contact us to discuss your project details — we'll provide a preliminary estimate within 24 hours.







