Event Types
Every interactive React component handles events: typing into an input, submitting a form, clicking a button. React ships its own synthetic event types that wrap the DOM events with consistent cross-browser behavior, and typing your handlers with them unlocks autocomplete on event.target, event.key, event.currentTarget, and friends. The trick is knowing which event type goes with which interaction and which element. This page covers ChangeEvent, FormEvent, MouseEvent, and the rest, plus when to skip the annotations entirely by letting JSX infer them.
The two ways to type a handler
You can annotate the event parameter directly, or annotate the whole function with the matching EventHandler type. The second form lets inference type the parameter for you.
import type { ChangeEvent, ChangeEventHandler } from "react";
// Style 1 — annotate the event parameter:
function handleChange(event: ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
// Style 2 — annotate the function; event is inferred:
const handleChange2: ChangeEventHandler<HTMLInputElement> = (event) => {
console.log(event.target.value);
};
Both produce the same checked handler. Reach for the …EventHandler<T> alias when assigning to a variable, and the explicit parameter type when declaring a standalone function.
Change events on inputs
ChangeEvent<T> types onChange for form controls. The element type parameter decides what event.target is — use HTMLInputElement, HTMLSelectElement, or HTMLTextAreaElement.
import { useState, type ChangeEvent } from "react";
function SearchBox() {
const [query, setQuery] = useState("");
function onChange(event: ChangeEvent<HTMLInputElement>) {
setQuery(event.target.value); // value is typed as string
}
return <input value={query} onChange={onChange} />;
}
Because event.target is an HTMLInputElement, .value is a string and properties like .checked exist only when the element is a checkbox. Picking the wrong element type (e.g. HTMLDivElement) makes .value disappear.
Form submission
FormEvent<T> types the onSubmit handler. Call event.preventDefault() to stop the browser’s full-page reload, then read the controlled values from state.
import type { FormEvent } from "react";
function LoginForm() {
function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const data = new FormData(event.currentTarget);
console.log(data.get("email"));
}
return (
<form onSubmit={onSubmit}>
<input name="email" />
<button type="submit">Sign in</button>
</form>
);
}
event.currentTarget is typed as HTMLFormElement (the element the handler is attached to), which is what lets new FormData(event.currentTarget) type-check.
Prefer
event.currentTargetoverevent.targetfor the element the handler is bound to.currentTargetis precisely typed;targetis the broaderEventTargetbecause the event may have originated on a descendant.
Mouse, keyboard, and focus events
Each interaction has its own event type, all generic over the element. The most common ones:
import type { MouseEvent, KeyboardEvent, FocusEvent } from "react";
function onClick(event: MouseEvent<HTMLButtonElement>) {
console.log(event.clientX, event.shiftKey);
}
function onKeyDown(event: KeyboardEvent<HTMLInputElement>) {
if (event.key === "Enter") event.currentTarget.blur();
}
function onBlur(event: FocusEvent<HTMLInputElement>) {
console.log(event.target.value);
}
| Interaction | Event type | Element parameter |
|---|---|---|
onChange | ChangeEvent<T> | input / select / textarea |
onSubmit | FormEvent<T> | HTMLFormElement |
onClick | MouseEvent<T> | HTMLButtonElement, etc. |
onKeyDown | KeyboardEvent<T> | the focused element |
onFocus / onBlur | FocusEvent<T> | the focused element |
Let JSX infer inline handlers
For handlers written inline in JSX, you usually do not need any annotation. React knows onClick on a <button> provides a MouseEvent<HTMLButtonElement>, so the parameter is inferred.
function Toolbar() {
return (
<button
onClick={(event) => {
// event: MouseEvent<HTMLButtonElement> — fully inferred
event.currentTarget.disabled = true;
}}
>
Save
</button>
);
}
This contextual typing only works inline. Once a handler is extracted into a named function, the parameter has no context to infer from, so you must annotate it explicitly.
Best Practices
- Use React’s synthetic event types (
ChangeEvent,FormEvent,MouseEvent), not the global DOM ones. - Always pass the element type parameter so
event.target/currentTargetis precise. - Prefer
event.currentTargetfor the handler’s own element; it is more strictly typed thantarget. - Let inline JSX handlers infer their event type; annotate only extracted functions.
- Use the
…EventHandler<T>aliases when assigning a handler to a typed variable. - Call
event.preventDefault()insideFormEventhandlers on controlled forms. - Import event types with
import typeto keep them out of the runtime bundle.