addons-frontend is the client half of the AMO stack, and its dev server proxies a real backend
Front-end to complement mozilla/addons-server
At a glance
- What is it?
- Mozilla's addons-frontend is a React and Redux front end for addons-server, and its most distinctive feature for developers is a set of dev server commands that proxy staging or dev data instead of faking it. It suits engineers working inside Mozilla's add-on ecosystem, and it comes with a licensing, security reporting and container setup that other projects do not have to carry.
- Who is it for?
- addons-frontend is the right codebase for an engineer who has to modify the AMO user interface, submit a change to addons-server's data model, or understand how Mozilla ships a large public service, because the dev server, the jest suite and the container build are all in one place.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The repository is a front end that expects a specific backend contract
addons-frontend is described in its own documentation as front-end infrastructure and code to complement mozilla/addons-server, and the package.json description calls it a set of universal front-end projects to complement addons-server. The pairing is the point. This repository holds the pages, the React components, the Redux store and the API client, while the other repository holds the data, the moderation queue and the API that answers them. Neither half is useful alone, and that dependency is what shapes the whole developer experience. The topics list react and redux, the source lives under `src/`, and the tests under `tests/` with a jest config at the root. The application is also universal in the sense of server-side rendering, since the container image copies locale catalogues into place and the Dockerfile comment explains that `src/amo/server/base.js` requires `src/locale/<locale>/amo.js` at SSR time to translate pages, so the same code produces HTML for crawlers and a client bundle for the browser.
Four amo commands decide whose data your dev server serves
The distinctive piece of tooling is the set of `amo:` scripts, and they differ along one axis only: which backend answers the API calls. `npm run amo:olympia` starts the dev server or proxy against data from a local addons-server environment, `npm run amo:dev` points it at the dev server at addons-dev.allizom.org, and `npm run amo:stage` points it at the staging server at addons.allizom.org. There is also `npm run amo:dev-https`, the same as the dev variant but over HTTPS, served at example.com:3000, with the documentation pointing at a separate page for setting up that environment. The practical consequence is that a new contributor does not need to stand up a backend to see real add-on data. They do need to understand which tier they are pointed at, because a UI change reviewed against staging data can look different from the same change against local data, and because the local `olympia` path is the only one that lets you change the data. The README's own getting-started instruction is simply to type `npm run amo:stage` to start a local server that connects to a hosted staging server.
The setup itself is short. The documentation asks for a Node LTS release, with npm as the package manager, and recommends nvm for keeping several Node versions side by side on a development machine. It also notes that contributors on Windows should follow the separate Windows guidelines in the docs directory, which is a reasonable signal that the development path is not identical on every platform. The install step is a single npm install, after which the amo command of your choice starts a server on your machine that talks to whichever backend tier you picked.
Bundle size is a checked artefact, not a hope
The repository treats payload size as a build-time gate. `npm run build` builds the app, `npm run build-ci` runs the build and then the bundlewatch script, and `npm run bundlewatch` runs bundlewatch to check the generated AMO bundle sizes, with the documentation noting that building AMO is required first. The check is implemented in the repository rather than assumed from a hosted service, since the script entry points at `bin/bundle-check.js`. A separate `build-check` script points at `bin/build-checks.js`, and the webpack layer is split into several configuration files for the dev, prod and blog-utils builds. There is also a `build:prebuilt` script, `bin/build-prebuilt-assets.js`, and the Docker build runs it with a `PREBUILD_ENVS` argument set to dev, stage, prod, which is how one image can carry the assets for every environment. For a reviewer, the practical implication is that a pull request which grows the bundle shows up as a bundlewatch failure rather than as a slow page noticed later.
Lint, format, types and tests are four separate npm entries
The tooling surface is wide because a project of this size has several kinds of static checking, and the package.json keeps them apart. `npm run eslint` lints the JavaScript and `npm run stylelint` lints the SCSS, and `npm run lint` runs both. Flow is run through `npm run flow`, with `flow:check` for an explicit check that exits, and `flow:dev` for a continuous mode that watches the flow config, the flow directory, stories, src and tests. Formatting is Prettier, with `npm run prettier` writing across the codebase, `npm run prettier-ci` failing when code is not formatted, and `npm run prettier-dev` comparing only modified files against the master branch through pretty-quick. The jest suite has its own variants: `npm test` enters watch mode, `npm run test-once` runs everything and exits, and `npm run test-coverage-once` produces a coverage report and exits. A `version-check` script verifies the required dependencies, and `npm run start-func-test-server` starts a Docker container for functional tests, which is the one place the project itself asks you to have Docker running.
Two commands cover most day-to-day work. Running everything and exiting is one line:
npm run test-onceAnd when you only care about one file, the runner takes a path:
npm test -- tests/unit/amo/components/TestAddon.jsBoth are quoted from the development command reference, and both behave as the names suggest: the first runs the whole suite and the linters, the second narrows jest to the files you touched.
The container runs as a non-root user on port 4000 with no build on boot
The Dockerfile is a two-stage build that separates dependency installation from the runtime image. The builder stage is node:22.22.2-slim, it installs python3, build-essential and git, runs `npm ci`, and then runs the prebuilt-assets build for the three environments. The runtime stage is the same base image with a dedicated account created with a fixed uid of 9500 and a home directory, and the working directory is explicitly chowned because the WORKDIR directive otherwise leaves ownership with root. The final image copies the node_modules from the builder, the prebuilt assets, and the server-side locale catalogues, then removes the local node_modules in favour of the ones from the builder stage. The runtime configuration is two environment variables:
ENV SERVER_HOST=0.0.0.0
ENV SERVER_PORT=4000The command selects the prebuilt assets matching the target environment and starts the app, and the comment is explicit that there is no build on boot. That last detail matters operationally, since a container that compiles on start is slow and fails in ways a prebuilt image does not.
Security reports go to Bugzilla, never to an issue tracker
The reporting policy is the most unusual thing in the documentation and the easiest to get wrong. The code and its associated production website are included in Mozilla's web and services bug bounty program, and the documentation asks that a security vulnerability be submitted through that program's process and FAQ pages, with further technical detail on a Bug Bounty Onramp page. It then states plainly that security-related bugs should be submitted through Bugzilla using the web security bug form, and never through a GitHub issue or by email. That last prohibition is not bureaucracy. A public issue tracker is exactly the wrong channel for a vulnerability in a browser add-on marketplace, and the rule exists because other channels have leaked. For anyone planning to work on this codebase, the practical advice is narrow and concrete: if your work touches input handling, rendering of untrusted add-on metadata, or the API client, raise it through the bounty process before you write a patch, and do not open a public issue to ask about it.
A client-only single page app against the same API is the alternative
The realistic alternative to this repository is not a different add-on marketplace but a different front-end strategy: a client-only single page application that talks to addons-server directly from the browser and ships no server-rendered HTML. The difference in approach shows up in three places. Translations come first, since this project generates server-side locale catalogues during the image build and has the server require the locale file at SSR time to translate pages, while a client-only app would have to load translations after boot and would show untranslated or partially translated text on the first paint. Deployment comes second, since this project ships one image carrying prebuilt assets for dev, stage and prod, selected at container start with no build on boot, while a client-only app would only need static files and could be pushed to any CDN or Pages deployment without a Node runtime in the image at all. Third, caching and authorisation are handled differently, because a universal app can serve a personalised page from the server without a separate token exchange, whereas a pure client app has to hold a session token in the browser and deal with cross-origin rules. The trade-off goes the other way too: the client-only approach is far less machinery for a small screen, and none of the Node, Docker or Flow tooling in this repository is needed. If your task is a small dashboard rather than the public AMO interface, the simpler approach wins.
Where this client stack stops being the right tool
Several constraints make addons-frontend a poor default for anything that is not the AMO user interface. The package is marked private in package.json, so it is not published for consumption and the install path is a repository clone rather than a package from a registry. The data contract is hard-wired to addons-server, so a different backend means rewriting the API client and the Redux state shape, not swapping a base URL. The test surface is large enough that a contributor needs the environment set up properly, and the Flow toolchain has a known platform wrinkle, since the documentation notes that the current version of Flow does not have default support for Silicon Macs and asks for Rosetta 2 to be installed on Apple Silicon machines. There is also a watch-mode papercut that the documentation calls out, where an `Error watching file for changes: EMFILE` message on macOS is usually fixed by installing watchman. None of these are blockers for a Mozilla contributor with the right machine, and all of them are reasons to pick a different template for a new side project.
Editorial conclusion
addons-frontend is the right codebase for an engineer who has to modify the AMO user interface, submit a change to addons-server's data model, or understand how Mozilla ships a large public service, because the dev server, the jest suite and the container build are all in one place. It is a poor fit as a starter kit for a generic React application, since the routing, the API client and the state shape all assume the addons-server schema, and the package is marked private in package.json, so it is not published for consumption. Before contributing, read the security reporting rules, because the project asks that vulnerabilities go to Bugzilla and never to a GitHub issue or an email, check that your Node version satisfies the `>=22` engine requirement rather than the generic LTS note in the README, and confirm with the maintainers whether the `amo:olympia` proxy against a local addons-server is a prerequisite for the change you plan.
Frequently asked questions
How do I run addons-frontend locally?
Install the dependencies with npm install, then start a local server with npm run amo:stage, which connects to the hosted staging server. The other options are npm run amo:dev for the dev server, npm run amo:dev-https for HTTPS, and npm run amo:olympia for data from a local addons-server environment.
How do I run the addons-frontend test suite?
npm test enters jest in watch mode, while npm run test-once runs everything and exits, and npm run test-coverage-once generates a coverage report before exiting. A single file can be targeted with npm test followed by the file path, and eslint checks can be skipped by setting NO_ESLINT=1.
Which Node version does addons-frontend require?
The package.json engines field requires Node 22 or newer, and the Docker build uses the node:22.22.2-slim base image. The README refers to an LTS release of Node generally and suggests nvm for managing multiple versions during development.
How should I report a security issue in addons-frontend?
The documentation asks for security bugs to be submitted through Mozilla's web and services bug bounty program, using the web security bug form in Bugzilla, and explicitly says never to submit them through a GitHub issue or by email.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mozilla-addons-frontend)Was this useful?