TypeScript React Event Types: Typing Handlers
In TypeScript React, an event handler is a function that receives a SyntheticEvent object and performs an action. To type a handler correctly, you must specify both the event type (the HTML element triggering the event) and the function signature. React's React.MouseEvent<HTMLButtonElement> is the standard pattern for button clicks, while React.ChangeEvent<HTMLInputElement> handles form inputs. Without proper typing, your handler won't autocomplete event properties, and you'll lose the ability to catch type errors at compile time.
How Do You Type a React Event Handler in TypeScript?
React event handlers are typed as function parameters or functional component props. The event type follows the pattern React.[EventName]<[HTMLElement]>, where the generic parameter specifies which HTML element triggers the event. For a button click handler, you write React.MouseEvent<HTMLButtonElement>, which tells TypeScript the handler receives a synthesized mouse event from a button. The handler itself is a function: (event: React.MouseEvent<HTMLButtonElement>) => void or a callback that returns a value. This approach ensures every property you access on the event object is valid.
Typing Click and Mouse Events
Click handlers on buttons are the most common interaction pattern. Here's how to type them correctly:
// Basic button click handler
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log("Button clicked");
event.preventDefault();
};
export const MyButton = () => (
<button onClick={handleClick}>Click me</button>
);
The React.MouseEvent<HTMLButtonElement> generic parameter tells TypeScript the event comes from a button. This means you can access event properties like currentTarget.textContent without type errors. If you try to access a property that doesn't exist on MouseEvent, TypeScript will warn you immediately. For other elements that support mouse events (like div or a), change the generic parameter: React.MouseEvent<HTMLDivElement> for div or React.MouseEvent<HTMLAnchorElement> for links.
// Click handler with multiple element types
const handleDivClick = (event: React.MouseEvent<HTMLDivElement>) => {
const x = event.clientX;
const y = event.clientY;
console.log(`Clicked at ${x}, ${y}`);
};
export const ClickableDiv = () => (
<div onClick={handleDivClick}>Hover and click</div>
);
Typing Form Input Change Events
Form inputs trigger change events when the user types or selects a value. The type React.ChangeEvent<HTMLInputElement> is used for text inputs, while React.ChangeEvent<HTMLTextAreaElement> is for textareas. This distinction is important because different form elements have slightly different APIs.
// Change handler for text input
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.currentTarget.value;
console.log("Input value:", value);
};
export const TextInput = () => (
<input type="text" onChange={handleChange} placeholder="Type here" />
);
The currentTarget property is the element that the listener is attached to, while target is the element where the event originated. In most cases, they're the same, but TypeScript requires you to be explicit. Using currentTarget is safer because it's always guaranteed to be the correct type—the generic parameter ensures it matches the HTML element you specified.
Typing Form Submit Handlers
Form submission uses React.FormEvent<HTMLFormElement>, not MouseEvent or ChangeEvent. This type is specific to form submissions and includes the submit event properties.
// Form submit handler
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
// Access form data
const formData = new FormData(event.currentTarget);
const name = formData.get("name");
console.log("Form submitted:", name);
};
export const MyForm = () => (
<form onSubmit={handleSubmit}>
<input type="text" name="name" placeholder="Your name" />
<button type="submit">Submit</button>
</form>
);
Generic Handler Types for Inline Definitions
If you define handlers inline within JSX, you can use an arrow function with an implicit type or explicitly annotate it:
// Implicit typing (TypeScript infers the type)
export const Counter = () => {
const handleIncrement = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log("Increment clicked");
};
return (
<div>
<button onClick={handleIncrement}>+1</button>
</div>
);
};
Key Takeaways
- Event handlers are typed as
React.[EventName]<[HTMLElement]>, where the generic parameter specifies the element type. - Use
React.MouseEvent<HTMLButtonElement>for click handlers,React.ChangeEvent<HTMLInputElement>for input changes, andReact.FormEvent<HTMLFormElement>for form submissions. - Always access event properties through
currentTargetto ensure type safety and consistency. - TypeScript will warn you if you access invalid properties on an event object, preventing runtime errors.
- Different form elements (input, textarea, select) require different generic parameters in ChangeEvent.
Frequently Asked Questions
What is the difference between target and currentTarget in React events?
target is the element that triggered the event, while currentTarget is the element the listener is attached to. In React, currentTarget is type-safe because its type matches your generic parameter. Use currentTarget in handlers for better type inference.
Do I need to type the event parameter if I don't use it?
No, if you don't access any properties on the event object, you can omit the type annotation. However, explicitly typing it is good practice for clarity and future modifications. Alternatively, use React.MouseEvent<HTMLButtonElement> with an underscore prefix: (_event) => { } to signal the unused parameter.
Can I type a handler that works with multiple element types?
Yes, use a union type: React.MouseEvent<HTMLButtonElement | HTMLDivElement>. However, a better approach is to create a separate, more generic handler that doesn't depend on a specific element type, or accept the event without a specific HTML element type constraint.
How do I type an async event handler?
An async handler returns a Promise instead of void. Type it as async (event: React.MouseEvent<HTMLButtonElement>): Promise<void> => { }. Always handle promise rejections with a .catch() or wrap in a try-catch, since unhandled promise rejections in event handlers are warnings, not errors.
What if TypeScript says the event type is wrong?
Double-check the HTML element you're attaching the listener to matches the generic parameter. For example, onSubmit must be on a <form>, not a <div>. If you're using a custom component, ensure it forwards the correct event type from the underlying HTML element.