How do you type events in React with TypeScript? For example, the `onChange` of an input or the `onSubmit` of a form.
Show answer — try answering out loud first
React ships its own generic event types: ChangeEvent<HTMLInputElement>, FormEvent<HTMLFormElement>, MouseEvent<HTMLButtonElement>, etc. The generic indicates which HTML element the event happens on.
React doesn't use the browser's native events directly: it wraps them in "synthetic events" so they work the same across all browsers. That's why the types don't come from the DOM, but from React's package: you import them from "react".
- The pattern is
EventType<HTMLElement>: the first name says what happened (change, submit, click) and the generic says on which element. - The most common ones:
ChangeEvent<HTMLInputElement>for inputs,FormEvent<HTMLFormElement>for a form's submit, andMouseEvent<HTMLButtonElement>for clicks on buttons. - Trick to avoid memorizing them: write the handler inline (
onChange={(e) => ...}) and TypeScript infers the type on its own. Then, if you want to extract the function, hover overeand copy the type the editor shows you. e.currentTargetis the element where you attached the handler and is always properly typed according to the generic.e.targetis where the event originated (it can be a child), so its type is more generic. On an input withonChangeyou can usee.target.valuewithout a problem; on containers, prefercurrentTarget.
import { useState } from "react";
import type { ChangeEvent, FormEvent, MouseEvent } from "react";
function FormularioContacto() {
const [correo, setCorreo] = useState("");
function manejarCambio(e: ChangeEvent<HTMLInputElement>) {
setCorreo(e.target.value); // value is string, properly typed
}
function manejarEnvio(e: FormEvent<HTMLFormElement>) {
e.preventDefault(); // prevents reloading the page
console.log("Enviando:", correo);
}
function manejarLimpiar(e: MouseEvent<HTMLButtonElement>) {
console.log("Botón:", e.currentTarget.name); // currentTarget properly typed
setCorreo("");
}
return (
<form onSubmit={manejarEnvio}>
<input type="email" value={correo} onChange={manejarCambio} />
{/* Trick: if the handler is inline, TypeScript infers the type of e on its own */}
<button type="button" name="limpiar" onClick={manejarLimpiar}>Limpiar</button>
<button type="submit">Enviar</button>
</form>
);
}
Typing the event with the browser's native type or with any:
// The native DOM type doesn't match React's synthetic event
function manejarCambio(e: Event) {
setCorreo(e.target.value);
// Error: Property 'value' does not exist on type 'EventTarget'.
}
The fix is to use React's type with the correct element:
import type { ChangeEvent } from "react";
function manejarCambio(e: ChangeEvent<HTMLInputElement>) {
setCorreo(e.target.value); // now TypeScript knows that value exists
}
"React wraps native events in synthetic events, so I use the types React exports:
ChangeEvent<HTMLInputElement>for inputs,FormEvent<HTMLFormElement>for forms, andMouseEvent<HTMLButtonElement>for clicks. The generic indicates the element where the handler goes. If I don't remember the exact name, I write the handler inline and TypeScript infers it; then I copy that type if I want to extract the function. I also mind the difference betweentarget, which is where the event originated, andcurrentTarget, which is the element with the handler and is always properly typed."
Create a Buscador component with a controlled input and a form. On submit, it should do preventDefault and print the searched text. Type the two handlers as separate functions (not inline).
See solution
import { useState } from "react";
import type { ChangeEvent, FormEvent } from "react";
function Buscador() {
const [texto, setTexto] = useState("");
function manejarCambio(e: ChangeEvent<HTMLInputElement>) {
setTexto(e.target.value);
}
function manejarEnvio(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
console.log("Buscando:", texto);
}
return (
<form onSubmit={manejarEnvio}>
<input value={texto} onChange={manejarCambio} placeholder="Buscar..." />
<button type="submit">Buscar</button>
</form>
);
}