# @testing-library/jest-dom: Custom Jest Matchers for DOM Assertions

> jest-dom adds declarative DOM matchers to Jest and Vitest, so assertions read like what the user sees instead of raw attribute checks. It is small, MIT licensed, and best adopted before your test suite grows its own helper functions.

**testing-library/jest-dom** — :owl: Custom jest matchers to test the state of the DOM

- Repository: https://github.com/testing-library/jest-dom
- Website: https://testing-library.com/docs/ecosystem-jest-dom
- Stars: 4,598 · Forks: 423
- Language: JavaScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/testing-library-jest-dom

## What jest-dom solves, and who ends up needing it

The README states the problem plainly: you want to use Jest to assert things about the state of a DOM, and you want to avoid the repetitive patterns that come with it, such as checking an element's attributes, text content or CSS classes. Without a matcher library, those checks turn into chains of property access and string comparison that drift as the component changes.

@testing-library/jest-dom supplies a set of custom matchers that extend Jest's expect. The README's own words are that they make tests "more declarative, clear to read and to maintain". That is the whole pitch, and it is a narrow one. If your tests never touch rendered DOM, this package has nothing to offer you.

The intended audience is anyone writing component tests with Jest or Vitest alongside a DOM query library. The README says the matchers work with any library or framework that returns DOM elements from queries, and its examples use queries such as getByTestId, queryByTestId and getByText. React Testing Library is the common pairing, but the matchers themselves are not React-specific.

## How the matchers attach to expect

The mechanism is Jest's expect.extend. The package ships a set of matcher functions, and importing the main entry point registers them on the global expect object. The README shows the standalone route for other runners: import the matchers as a namespace and call expect.extend(matchers) yourself.

The repository layout reflects that split. There are separate entry files at the top level (jest-globals.js, matchers.js, vitest.js) and matching type declaration files (jest-globals.d.ts, matchers.d.ts, vitest.d.ts), with the source in src/ and the build produced by rollup.config.js. The package.json exports map gives each entry its own require and import condition plus a types path, so the subpath you import determines both the runtime file and the TypeScript declarations you get.

That design has a consequence worth stating: the import path is not cosmetic. Importing the plain entry point when you run Jest with injectGlobals: false, or under Vitest, wires the matchers to the wrong expect instance. The README addresses each of those cases with a dedicated import rather than a configuration flag.

## Installing jest-dom and writing a first assertion

The README distributes the module through npm as a devDependency. It also recommends the companion eslint plugin, eslint-plugin-jest-dom, which the README says provides auto-fixable lint rules that prevent false positive tests and improve readability by ensuring you use the right matchers.

Install it with npm or yarn:

```bash
npm install --save-dev @testing-library/jest-dom
```

or, with yarn:

```bash
yarn add --dev @testing-library/jest-dom
```

Next, import it once in a setup file. The README calls this the "tests setup file" and links to Jest's setupFilesAfterEnv option:

```javascript
// In your own jest-setup.js (or any other name)
import '@testing-library/jest-dom'

// In jest.config.js add (if you haven't already)
setupFilesAfterEnv: ['<rootDir>/jest-setup.js']
```

Under Vitest the README says the module works as-is but needs a different import, placed in the setupFiles property of your Vitest config:

```javascript
// In your own vitest-setup.js (or any other name)
import '@testing-library/jest-dom/vitest'

// In vitest.config.js add (if you haven't already)
setupFiles: ['./vitest-setup.js']
```

If you use @jest/globals with injectGlobals: false, the README gives a third path, import '@testing-library/jest-dom/jest-globals' in the setup file. With TypeScript, the README asks that the setup file be .ts rather than .js so the types are included, and that the file appear in the include array of tsconfig.json. For Vitest with TypeScript the README also shows adding "vitest/globals" and "@testing-library/jest-dom" to compilerOptions.types. After the setup file is registered, matchers such as toBeInTheDocument, toHaveTextContent and toBeVisible are available on expect without further imports.

## The matcher list is broad, and that breadth is the maintenance surface

The table of contents lists around thirty matchers. They split into rough groups: visibility and state (toBeVisible, toBeDisabled, toBeEnabled, toBeChecked, toBePartiallyChecked, toBePressed, toBePartiallyPressed), content (toHaveTextContent, toContainHTML, toContainElement, toBeEmptyDOMElement), attributes and classes (toHaveAttribute, toHaveClass, toHaveStyle, toHaveRole), form state (toHaveValue, toHaveDisplayValue, toHaveFormValues, toBeRequired, toBeValid, toBeInvalid), focus and selection (toHaveFocus, toHaveSelection), accessibility (toHaveAccessibleName, toHaveAccessibleDescription, toHaveAccessibleErrorMessage), and ordering (toAppearBefore, toAppearAfter, plus toContainAnyBy* and toContainOneBy*).

Three matchers are marked deprecated: toBeEmpty, toBeInTheDOM and toHaveDescription. If you are migrating an older suite, those are the names to search for first, because the README keeps them documented but flags them.

A list this long cuts both ways. It means you rarely write a custom matcher for a DOM question, and it also means the package tracks a moving target. Browsers change accessible-name computation, jsdom changes what it implements, and each of those changes can move a matcher's result. That is the cost of having the assertion live in a library instead of in your test file.

## Where jest-dom is the wrong tool

The largest limitation is the environment, not the matchers. The package asserts against a DOM object, and in Jest that object is normally produced by jsdom. jsdom is a JavaScript reimplementation, not a browser. Layout is the clearest casualty: the README documents toBeVisible, and a matcher of that name invites the reading that an element hidden by a stylesheet cascade, by a zero-height flex parent, or by an off-screen transform will be caught. Anything that depends on real layout computation is outside what a simulated DOM can answer.

A second boundary is scope. These are assertions, not a test runner and not a query library. jest-dom gives you nothing for finding an element, nothing for firing events, and nothing for waiting on asynchronous UI. If you expected the package to replace a browser automation tool, it will not.

The third is version pressure. The package.json engines field requires Node >=22, npm >=6 and yarn >=1. A team pinned to an older Node runtime cannot take the current release without upgrading the runtime first, and that upgrade may be larger than the test change that prompted it.

## jest-dom against a browser-driven runner such as Cypress

The natural alternative, and the one the search data keeps asking about, is a browser-driven test runner such as Cypress. The difference is where the test executes. jest-dom runs inside the JavaScript process that holds the simulated DOM, so assertions are fast, synchronous to write, and can inspect an element that was never painted. Cypress drives a real browser, so layout, real event dispatch and cross-browser rendering are all observable.

That difference decides the split. A visibility assertion in jest-dom is a statement about the simulated DOM's notion of the element; the same assertion in a browser runner is a statement about what was rendered. Both are useful, and they are not substitutes. Teams that need to know whether a button is actually clickable at a given viewport end up in the browser runner regardless of how many matchers jest-dom provides.

Within the Jest ecosystem there is also the escape hatch the README documents for other runners: import * as matchers from '@testing-library/jest-dom/matchers' and call expect.extend(matchers) against your own expect. That is the route to take when your runner is Jest-compatible but not Jest, rather than assuming the main entry point will register correctly.

## Licence, releases and the cost of keeping up

The project is MIT licensed, and the README links to the licence file. For most teams that means the usual permissive terms apply: the package can be used in closed-source projects with the copyright notice retained. This is a description of the licence identifier, not legal advice; if your organisation has a policy review for dependencies, the MIT text is short and the repository includes it at the root as LICENSE.

On releases, the recent tags are v7.0.1 on 2026-08-09, v7.0.0 on 2026-07-20 and v6.10.0 on 2026-07-20. A major version bump landing in the same window as a minor on the previous line is worth reading the CHANGELOG for before you upgrade, because major versions of an assertion library can change what a matcher accepts or how strictly it evaluates. The repository keeps a CHANGELOG.md at the root, so that file is the place to look rather than the release tag alone.

The last push to the repository was on 2026-08-09, which is within the last few months, so the project is not dormant. That said, the upgrade cost here is genuinely low for most suites: the surface is a set of matcher functions behind one import, and the setup file is a single line. The real work in a version bump is auditing tests that depend on matcher semantics, not rewriting application code.

## Conclusion

Adopt jest-dom if you assert on rendered DOM with Jest or Vitest and want matchers such as toBeVisible, toHaveTextContent or toHaveAccessibleName instead of hand-written attribute checks. Skip it if your tests only exercise pure functions, or if you need browser-level behaviour that jsdom does not implement. Before installing, check your Node version against the engines field (>=22) and confirm which import path matches your runner: '@testing-library/jest-dom', '@testing-library/jest-dom/jest-globals' or '@testing-library/jest-dom/vitest'. The package.json exports map lists all three, so the choice is a config decision you make once.

## FAQ

### How do I install @testing-library/jest-dom?

Install it as a devDependency with npm install --save-dev @testing-library/jest-dom or yarn add --dev @testing-library/jest-dom. Then import it once in a Jest setup file registered through setupFilesAfterEnv. The README also recommends the companion eslint-plugin-jest-dom.

### What is @testing-library/jest-dom?

It is a set of custom Jest matchers that extend expect so you can assert on the state of a DOM, covering attributes, text content, CSS classes, form values, focus and accessible names. The README describes the goal as making tests more declarative and easier to maintain.

### Does @testing-library/jest-dom work with Vitest?

Yes. The README says the module works as-is under Vitest but needs a different import, '@testing-library/jest-dom/vitest', added to the setupFiles property in the Vitest config. TypeScript users may also need to add 'vitest/globals' and '@testing-library/jest-dom' to compilerOptions.types.

### Is there an alternative to @testing-library/jest-dom?

The README documents a standalone route for runners that are Jest-compatible but not Jest: import the matchers namespace from '@testing-library/jest-dom/matchers' and call expect.extend(matchers) against your own expect. For tests that need real browser layout rather than a simulated DOM, a browser-driven runner such as Cypress is a different kind of tool, not a drop-in replacement.

## Sources

- [License: MIT](https://github.com/testing-library/jest-dom/blob/main/LICENSE)
- [Project website](https://testing-library.com/docs/ecosystem-jest-dom)
- [README](https://github.com/testing-library/jest-dom/blob/main/README.md)
- [Releases](https://github.com/testing-library/jest-dom/releases)
- [testing-library/jest-dom on GitHub](https://github.com/testing-library/jest-dom)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/testing-library-jest-dom
