This guide shows how to test user events in React with the user-event library. The library simulates interactions the way a browser performs them, so tests cover the full sequence of focus, pointer, and keyboard work that a real user triggers. Components often rely on focus, key events, or pointer state that a single dispatched event does not reproduce.
Start with setup()
Install user-event as a development dependency.
npm install --save-dev @testing-library/user-eventCall setup before rendering. The returned object holds one shared input device state, so consecutive actions behave like one user interacting with the page.
import { useState } from "react";
export default function Toggle() {
const [on, setOn] = useState(false);
return <button onClick={() => setOn(!on)}>{on ? "On" : "Off"}</button>;
}The click method performs the focus and pointer work that a real click involves, so the component receives the same sequence of events a browser would send.
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Toggle from "./Toggle";
test("toggles the label when clicked", async () => {
const user = userEvent.setup();
render(<Toggle />);
await user.click(screen.getByRole("button", { name: "Off" }));
expect(screen.getByRole("button", { name: "On" })).toBeVisible();
});Every method returns a promise, so await each interaction. The setup form is recommended over calling the default export directly, which exists mainly to ease migration from older versions.
Type into an input
Typing focuses the field, sends key events, and updates the value. This component echoes the typed name into a greeting.
import { useState } from "react";
export default function NameInput() {
const [name, setName] = useState("");
return (
<div>
<label htmlFor="name">Name</label>
<input id="name" value={name} onChange={(e) => setName(e.target.value)} />
<p>Hello, {name}</p>
</div>
);
}The type method sends each character the way a keyboard would, so the component updates state and the greeting appears.
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import NameInput from "./NameInput";
test("types into the input and shows the greeting", async () => {
const user = userEvent.setup();
render(<NameInput />);
await user.type(screen.getByLabelText("Name"), "Ada");
expect(screen.getByText("Hello, Ada")).toBeVisible();
});The assertion reads the visible greeting that appears after typing. It does not inspect the input value directly, because the user sees the greeting, not the state. The clear method does the opposite of typing, selecting the current value and removing it with the same events a backspace sequence would fire.
Keyboard and focus
The keyboard method sends named key presses, and the tab method moves focus the way the Tab key does.
test("moves focus with the keyboard", async () => {
const user = userEvent.setup();
render(<Form />);
await user.tab();
expect(screen.getByLabelText("First name")).toHaveFocus();
});This verifies the focus order a keyboard user experiences. It fails if the first control is not reachable, which is exactly the accessibility regression the test should catch. For the interaction patterns behind that concern, see how to make clickable elements accessible in React.
More interactions
The library covers more than clicks and typing. A short list of common methods and when to reach for them:
- doubleClick for actions that require a double click, such as opening a row in place.
- clear for removing the current value of a text control.
- selectOptions for choosing a value from a dropdown or multi-select.
- hover and unhover for components that reveal content on pointer enter and leave.
- upload for populating a file input with one or more files.
- paste for reading text from the clipboard into a field.
Each method models the whole interaction sequence rather than a single event, which keeps the same behavior-first guarantee.
Why not fireEvent
The fireEvent helper dispatches a single DOM event and stops there. A real interaction does more.
Typing focuses the field, fires keydown and keypress events, updates the value, and fires input. Using fireEvent to change a value skips all of that, so a component that depends on focus or key events can pass a test and still break in a real browser.
User-event also checks interactability before it acts. It refuses to click a hidden button or type into a disabled input, which matches what a real user can do. That check is the main reason a test that passes with fireEvent can fail when it is rewritten with user-event.
Use user-event for normal behavior, and reserve fireEvent for interactions the library cannot yet describe, such as uncommon media or drag edge cases.
Common mistakes
- Forgetting await, which lets the test finish before React processes the event.
- Using fireEvent.change to bypass the typing sequence.
- Querying by test ID instead of the label or role the user actually uses.
- Calling setup inside a beforeEach hook instead of inside the test.
What to learn next
Interactions pair naturally with queries. Review how to query elements in React Testing Library, then see the same ideas applied to form testing.
Rune AI
Key Insights
- Install @testing-library/user-event and call userEvent.setup() before render.
- Prefer user-event over fireEvent for realistic click and typing sequences.
- await each interaction, because user-event methods return promises.
- Use roles and labels so interactions hit the elements a user can reach.
- Reach for fireEvent only for interactions user-event cannot yet model.
Frequently Asked Questions
Should I use user-event or fireEvent?
Why call setup() before rendering?
Conclusion
user-event describes interactions instead of raw events. Call setup() once, query by role or label, then click, type, and tab your way through a test like a user.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.