# axe-core: the accessibility engine you run inside your existing test suite

> axe-core is a JavaScript accessibility engine from Deque Systems that runs in the browser or in JSDOM and returns violations, passes and incomplete results. It is built for teams that already have automated tests and want accessibility checks in the same run.

**dequelabs/axe-core** — Accessibility engine for automated Web UI testing

- Repository: https://github.com/dequelabs/axe-core
- Website: https://www.deque.com/axe/
- Stars: 7,578 · Forks: 947
- Language: HTML
- License: MPL-2.0
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/dequelabs-axe-core

## What axe-core is for, and who ends up using it

axe-core is an accessibility testing engine for websites and other HTML-based user interfaces. It is a library, not a scanner you point at a URL. You load it into a page or a DOM and call it, and it returns structured results. The README frames the audience as developers who already run unit, integration, browser or acceptance tests and want accessibility checks to happen in the same place. Deque Systems, an accessibility vendor, maintains it, and the project is open source under MPL-2.0.

The rules cover WCAG 2.0, 2.1 and 2.2 at levels A, AA and AAA, plus a set of best practices such as checking that every page has an h1 heading and catching ARIA attributes that will be ignored. The full list is grouped by WCAG level in doc/rule-descriptions.md. The honest number to hold onto is in the README: axe-core finds on average 57% of WCAG issues automatically. The rest is not silently dropped. Elements axe-core cannot be certain about come back as incomplete, which the README says need manual review. That split shapes how you should schedule the work.

## How the engine decides what to run

The mechanism is a JavaScript file evaluated against a live DOM. The package ships axe.js and axe.min.js; you include one of them in the page under test, then call axe.run() at the point where a new piece of UI becomes visible. The engine inspects the document, applies its rule set, and resolves a promise with a results object. Your test code decides what to do with it, which in the README example means throwing when results.violations is non-empty.

Two design decisions are worth noticing. First, the README states that axe automatically determines which rules to run based on the evaluation context, so you do not hand-assemble a rule list for every page. Second, the engine supports in-memory fixtures, static fixtures, integration tests, and iframes of infinite depth, which is why the getting-started instructions tell you to include the script in each iframe of your fixtures rather than once at the top. That iframe requirement is easy to miss and produces empty results if you skip it.

Results are not a boolean. Violations, passes and incomplete are separate, and the incomplete bucket is where the engine admits uncertainty. Treating incomplete as a pass is the most common way a suite looks green while real problems remain.

## Installing axe-core and running a first check

The README gives npm and pnpm as the two install paths. Both add it as a development dependency, which matches its role as a test-time tool rather than a runtime library shipped to users.

```bash
npm install axe-core --save-dev
```

With pnpm, the equivalent command in the README is pnpm add --save-dev axe-core. The package.json lists pnpm@11.17.0 under packageManager, so the repository itself is built with pnpm.

Next, include the built script in each iframe of your fixtures or test system, using the path the README shows:

```html
<script src="node_modules/axe-core/axe.min.js"></script>
```

Then call the engine wherever a new piece of UI becomes visible or exposed. The README's example rejects the promise when violations are present:

```js
axe
  .run()
  .then(results => {
    if (results.violations.length) {
      throw new Error('Accessibility issues found');
    }
  })
  .catch(err => {
    console.error('Something bad happened:', err.message);
  });
```

What you should see after a run is a results object; in a failing case the thrown error surfaces in your test output. The README does not document rollback or a baseline mode, so if you need to accept existing violations while blocking new ones, that logic is yours to write around the results object.

For editor-level feedback before a test ever runs, the README points at the axe-linter VS Code extension. It is a separate product from the engine, not part of the npm package.

## The 57% ceiling and the JSDOM gap

The README is unusually direct about coverage: on average 57% of WCAG issues are found automatically. That is the limitation to plan around. A green axe-core run is evidence that a specific rule set passed, not that the page is accessible. Anything requiring judgement, such as whether alt text is meaningful or whether a heading order reflects the content, sits outside what the engine can decide.

Environment support is the second constraint. The API fully supports Edge v40 and above, Chrome v42 and above, Firefox v38 and above, and Safari v7 and above, with Internet Explorer v11 marked deprecated. Only Chrome and Firefox are tested on every pull request, so the other browsers are supported in the sense that bugs get fixed rather than caught before merge.

JSDOM is the sharper edge. The README describes support as limited, says the project will attempt to make rules compatible, and recommends turning off rules that cannot work there. It names one concretely: the color-contrast rule is known not to work with JSDOM. If your test suite is node-only with no real browser, that is a rule you cannot rely on, and any suite that silently includes it will report nothing useful for contrast.

The README also states that axe-core does not support the deprecated v0 Shadow DOM implementation, and that it can only support environments where features are natively supported or polyfilled correctly.

## axe-core against Lighthouse and pa11y

Lighthouse is the comparison most people reach for, and the search data shows it. The difference is packaging and audience. Lighthouse is a page-audit tool: you give it a URL and it produces a report including accessibility results. axe-core is an engine you embed in tests you already own, and the README's whole argument is about timing, that accessibility tools meant to run at the end of development give unclear results and cause delays near release. If your goal is a one-off audit report, Lighthouse fits that shape better. If your goal is a failing test on every pull request, axe-core fits.

pa11y is a closer neighbour: a command-line accessibility runner rather than an embedded engine. The distinction is where the check lives. A CLI runner is a separate step in CI with its own invocation and output format; axe-core runs inside the test process and returns results your existing assertions can consume. That also means axe-core inherits your test environment's constraints, including the JSDOM limitation above, which a browser-driven CLI does not share.

None of these replace manual review. The README itself recommends combining the engine with guided tests in the axe browser extension to improve coverage, which is an admission that the automated layer is partial.

## Licence, localisation and the cost of staying current

axe-core is MPL-2.0. The npm package ships LICENSE-3RD-PARTY.txt alongside the main licence file, so third-party terms are bundled with the distribution. File-level copyleft under MPL-2.0 applies to modified files of the covered source; using the engine as a dependency in your test suite is a different question from modifying axe-core itself. If you fork or patch it, read the licence text and get your own advice rather than relying on a summary.

The upgrade cost is mostly rule churn. Releases move quickly: v4.12.0 on 2026-06-01, v4.12.1 on 2026-06-10, v4.13.0 on 2026-08-05, and the last push to the develop branch was on 2026-09-21. New rules and changed rules mean a suite that passed yesterday can fail after a version bump for a reason unrelated to your code. The repository keeps doc/rule-descriptions.md and regenerates it as part of the release process, which is the file to diff when a bump changes results. The package.json also shows a postbump step that runs pnpm ci and updates the subresource integrity history, so the published artifacts are tracked for integrity across versions.

Localisation is a build-time concern, not a runtime one. You add a file named <langcode>.json to ./locales, then build with pnpm run build -- --lang=nl, which produces axe.nl.js and axe.nl.min.js. Passing --all-lang builds every locale, and a comma-separated list such as --lang=nl,ja builds several. To start a translation, pnpm run translate -- --lang=<langcode> writes a JSON file into ./locales with the English text in place. This matters if your product ships in a language other than English and you want violation messages your team can read.

## Conclusion

Adopt axe-core if you already run browser or JSDOM tests and want accessibility violations to fail the same pipeline. Do not adopt it as your only accessibility process: the README states it finds on average 57% of WCAG issues automatically, and the rest needs manual review. Before rolling it out, verify which rules fire in your environment, because the README notes that the color-contrast rule is known not to work with JSDOM, and confirm how you will handle the incomplete results axe-core returns.

## FAQ

### What is axe-core used for?

It is an accessibility testing engine for websites and other HTML-based user interfaces. It runs inside your existing functional tests and returns violations, passes and incomplete results for WCAG 2.0, 2.1 and 2.2 rules plus best practices.

### Does Lighthouse use axe-core?

The README does not state that Lighthouse uses axe-core, so this cannot be confirmed from the project's own documentation. What the README does describe is axe-core as an engine you embed in your tests, which is a different delivery model from Lighthouse's page-audit reports.

### How do I install axe-core?

The README gives npm install axe-core --save-dev, or pnpm add --save-dev axe-core. After that you include axe.min.js in each iframe of your fixtures and call axe.run() where new UI becomes visible.

### How do I use axe-core?

Include the script in each iframe of your fixtures or test system, then call axe.run() at each point where a new piece of UI becomes visible or exposed. The promise resolves with a results object, and the README's example throws when results.violations is non-empty.

### Is axe-core free and open source?

Yes. The repository is licensed MPL-2.0 and the README states that axe is open source. The npm package also ships a LICENSE-3RD-PARTY.txt file for bundled third-party terms.

## Sources

- [dequelabs/axe-core on GitHub](https://github.com/dequelabs/axe-core)
- [License: MPL-2.0](https://github.com/dequelabs/axe-core/blob/develop/LICENSE)
- [Project website](https://www.deque.com/axe/)
- [README](https://github.com/dequelabs/axe-core/blob/develop/README.md)
- [Releases](https://github.com/dequelabs/axe-core/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dequelabs-axe-core
