React Testing Library: what it does, how to install it, and when it is the wrong tool
🐐 Simple and complete React DOM testing utilities that encourage good testing practices.
At a glance
- What is it?
- React Testing Library is a DOM testing utility built on react-dom that pushes tests toward user-visible behaviour. This article covers its query model, its install path (including the @testing-library/dom requirement from v16), its real limits, and how it differs from Jest and Vitest.
- Who is it for?
- Adopt React Testing Library if your React codebase needs tests that survive refactors and your team is willing to write assertions against roles, labels and text rather than component internals. Skip it if you need end-to-end coverage of a real browser, network and backend, or if you are pinned to React 17 or older, where the README points to version 12 instead.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 34 days 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 September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem React Testing Library was written to solve
The README frames the problem directly: you want maintainable tests for React components, and you want those tests to avoid implementation details so that refactoring a component's internals does not break the suite. That is a specific complaint about how React component tests were commonly written before this library existed. A test that reaches into component state, calls a method by name, or asserts on a shallow render tree is coupled to how the component is built. Rename a handler or move state into a hook and the test fails even though the rendered output is unchanged.
The library's stated guiding principle is that "the more your tests resemble the way your software is used, the more confidence they can give you." Everything else in the API follows from that. Queries are addressed to the rendered DOM, not to the component instance, so the test's vocabulary is the vocabulary a user or an assistive technology would encounter: a label, a role, a piece of visible text.
The audience is React application teams that already have a test runner and want the component-level layer to be less brittle. It is not aimed at people testing non-React DOM code, and it is not a replacement for a runner. The README treats it as a devDependency, a utility layer that sits inside Jest, Vitest or whatever else executes the test file.
How the render, screen and query mechanism actually fits together
The library is described in the README as a lightweight set of utility functions built on top of react-dom and react-dom/test-utils. That is the whole architecture: there is no custom renderer and no virtual DOM of its own. render() mounts your component through React's own DOM renderer, and the queries then search the resulting document.
The query naming convention is the part worth internalising before writing a single test. The README states that query* functions return the element or null if it cannot be found, while get* functions return the element or throw if it cannot be found. That single distinction decides how a test fails. A getByText call that cannot find the node throws immediately at the call site, which reads as a clear assertion failure. A queryByText call returns null, which is what you want when the absence of an element is the thing under test, as in the README's example where expect(screen.queryByText(testMessage)).toBeNull() asserts the message is not yet rendered.
screen is a convenience handle onto the document body that the README's basic example uses throughout, and the queries accept a regular expression as well as a string. The README notes that regex selectors make selectors more resilient to content tweaks. That is a real trade-off rather than a free win: a loose regex survives copy edits but can also match an element you did not intend, so the resilience and the specificity pull against each other.
Events come from fireEvent in the basic example, and the README also points to @testing-library/jest-dom for custom matchers such as toBeInTheDocument. The README is explicit that jest-dom is recommended but not required, and shows toBeDefined as the fallback.
Installing React Testing Library and writing a first test
The README distributes the module through npm and says it should be installed as a devDependency. From version 16 onward it also requires @testing-library/dom as a separate install, which is a change worth reading twice if you are upgrading an existing project:
npm install --save-dev @testing-library/react @testing-library/domThe yarn equivalent given in the README is yarn add --dev @testing-library/react @testing-library/dom. The package lists peerDependencies for react, react-dom and, from v16, @testing-library/dom, so your project supplies those itself. package.json declares an engines field of node >=18.
The README also states that versions 13 and above require React 18, and that projects on an older React should install version 12 with npm install --save-dev @testing-library/react@12. That is the version boundary to check before you copy any snippet from a recent tutorial.
The README's basic example renders a small component and asserts on visible text. The shape of a first test looks like this:
import '@testing-library/jest-dom'
import * as React from 'react'
import {render, fireEvent, screen} from '@testing-library/react'
import HiddenMessage from '../hidden-message'
test('shows the children when the checkbox is checked', () => {
const testMessage = 'Test Message'
render(<HiddenMessage>{testMessage}</HiddenMessage>)
expect(screen.queryByText(testMessage)).toBeNull()
fireEvent.click(screen.getByLabelText(/show/i))
expect(screen.getByText(testMessage)).toBeInTheDocument()
})The README notes that these imports are normally configured to be automatic through Jest setup, and links to the setup docs for cleanup. What you should see when this runs is a passing test whose assertions never mention component state or internal method names. If getByLabelText throws instead, the label text or the htmlFor wiring in your component is the first place to look, not the test.
The README's own comment in that example is worth repeating: the tests are the same whether the component is written with hooks or as a class.
Where React Testing Library stops being the right tool
The most common mismatch is scope. This library renders React into a DOM environment, typically jsdom, and asserts on what that environment produces. It does not exercise a real browser, real layout, real network conditions or a real backend. A test that passes here says nothing about whether the page works in Safari, whether the request actually reaches your API, or whether the CSS hides the button the query found. Teams that adopt it expecting end-to-end confidence end up with a large suite that is fast and green while production breaks in ways the suite cannot see.
The second limitation is version coupling. The README states plainly that versions 13 and above require React 18. If your application is on an older React, the documented path is to install version 12 rather than the current release. That means you are not receiving the current line of fixes, and any example you find online that assumes v16 behaviour, including the separate @testing-library/dom install, will not apply to you.
The third is the act() warning. The README documents a known compatibility issue with React DOM 16.8 where an update inside a test is not wrapped in act(...). The README's recommended workaround is to suppress the warning by patching console.error in a beforeAll and restoring it in an afterAll:
const originalError = console.error
beforeAll(() => {
console.error = (...args) => {
if (/Warning.*not wrapped in act/.test(args[0])) {
return
}
originalError.call(console, ...args)
}
})
afterAll(() => {
console.error = originalError
})It is honest of the README to label this a hack, and it is one. Silencing the warning does not fix the underlying unwrapped update; it only removes the symptom. The README's own framing is that this holds you over until you upgrade to React DOM 16.9.
React Testing Library versus Jest and Vitest, and the queries question
The comparison people reach for most often is React Testing Library versus Jest, and it is the wrong comparison. They occupy different layers. Jest is a test runner: it discovers files, provides test, expect and the assertion library, and executes the suite. React Testing Library provides render, the queries and event helpers, and expects a runner to be hosting it. You can run React Testing Library under Jest or under Vitest, and the README links to a TestingJavaScript.com course rather than prescribing a runner. If you are choosing a runner, that decision is independent of whether you adopt this library.
Vitest is the alternative worth naming because it changes the execution model rather than the assertions. Vitest is a Vite-native runner, so it reuses the same transform pipeline and module resolution as your Vite build instead of maintaining a separate Jest transform configuration. The practical difference is configuration surface, not test semantics: your render and query calls look the same. If your project already builds with Vite, that shared pipeline removes a class of duplicate config. If it builds with webpack, Vitest buys you less.
The queries are the other axis. The README's basic example uses getByLabelText and getByText, and the library ships a wider family of query variants. The naming convention the README states is the one to hold onto: query* returns null, get* throws. Choosing between them is choosing what a failure looks like, and choosing a role- or label-based query over a text query is choosing how much the test survives a copy change.
Maintenance status, licence and upgrade cost
The repository is not archived, and the last push was on 2026-08-27. The most recent release is v16.3.3, dated the same day, following v16.3.2 in January 2026 and v16.3.1 in December 2025. Release cadence over that window is steady but not fast, which fits a library whose API has been stable for several major versions.
The licence is MIT, which is permissive and imposes no copyleft obligation on your own code. That is a statement about the licence text, not legal advice about your situation; if your organisation has a policy on third-party dependencies, route it through whoever owns that policy.
The upgrade cost that actually bites is the v16 dependency split. Installing @testing-library/react no longer pulls in @testing-library/dom by itself, so a project that upgrades without adding the second package will hit a resolution error rather than a subtle failure. The React 18 requirement for v13 and above is the other boundary, and the README's answer for older React versions is to stay on version 12. Read the CHANGELOG.md at the repository root before a major bump; the README does not document rollback, so a downgrade path is something you would have to establish from your own lockfile.
Editorial conclusion
Adopt React Testing Library if your React codebase needs tests that survive refactors and your team is willing to write assertions against roles, labels and text rather than component internals. Skip it if you need end-to-end coverage of a real browser, network and backend, or if you are pinned to React 17 or older, where the README points to version 12 instead. Before committing, verify three things in your own setup: that @testing-library/dom is installed alongside @testing-library/react if you are on v16 or later, that your runner's cleanup runs after each test, and that your Node version satisfies the engines field, which package.json declares as node >=18.
Frequently asked questions
Is React Testing Library the same as Jest?
No. Jest is a test runner that discovers files and provides test, expect and the assertion library, while React Testing Library provides render, the queries and event helpers on top of react-dom. The library expects a runner to host it, and the README does not prescribe which one.
How do I install React Testing Library?
Install it as a devDependency with npm install --save-dev @testing-library/react @testing-library/dom, or the yarn equivalent yarn add --dev @testing-library/react @testing-library/dom. The README states that from version 16 onward @testing-library/dom is a separate required install, and package.json declares node >=18.
What are React testing libraries?
In this repository's terms, React Testing Library is a lightweight set of utility functions built on react-dom and react-dom/test-utils that encourage tests to avoid implementation details. Its stated guiding principle is that the more your tests resemble the way your software is used, the more confidence they can give you.
How do I use React Testing Library?
The README's basic example imports render, fireEvent and screen from @testing-library/react, calls render with your component, then asserts with queries such as screen.queryByText and screen.getByLabelText. The README notes that query* functions return the element or null, while get* functions return the element or throw.
What is React Testing Library?
It is a set of React DOM testing utilities distributed as @testing-library/react that encourage good testing practices, built as light utility functions on top of react-dom and react-dom/test-utils. Its guiding principle is that the more your tests resemble the way your software is used, the more confidence they can give you.
How do I run React Testing Library?
The library does not run tests itself; it provides render, queries and event helpers that execute inside a test runner such as Jest or Vitest. The README's basic example wraps the calls in a test() block and asserts with expect, which comes from the runner.
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/testing-library-react-testing-library)