Introduction
Event handling is how your React application responds to user interactions — clicks, key presses, form submissions, mouse movements, and more. While the concept is similar to vanilla JavaScript event handling, React adds its own conventions and optimizations. Events use camelCase naming, handlers are functions (not strings), and React wraps native browser events in a cross-browser compatible SyntheticEvent system. Understanding these differences is essential for building interactive React applications.
Key Concepts
- camelCase naming: React events use camelCase (
onClick,onChange,onSubmit) instead of lowercase HTML attributes (onclick,onchange). - Function references: Event handlers are JavaScript functions passed as references, not strings:
onClick={handleClick}notonclick="handleClick()". - SyntheticEvent: React wraps the native browser event in a SyntheticEvent object that provides a consistent API across all browsers.
- Prevent default: Use
e.preventDefault()instead ofreturn falseto prevent default browser behavior.
Real World Context
Consider a shopping cart where users click "Add to Cart" buttons, hover over products for quick previews, press keyboard shortcuts for navigation, and submit checkout forms. Each of these interactions requires a different event handler. React's unified event system means you write the same patterns regardless of the event type, and the SyntheticEvent wrapper ensures consistent behavior across Chrome, Firefox, Safari, and Edge.
Deep Dive
Basic event handling follows a simple pattern:
tsxfunction Button() { const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => { e.preventDefault(); console.log("Button clicked!"); }; return <button onClick={handleClick}>Click me</button>; }
Passing arguments to event handlers requires a wrapper function:
tsxfunction ItemList({ items }: { items: Item[] }) { const handleDelete = (id: string) => { console.log("Deleting item:", id); }; return ( <ul> {items.map((item) => ( <li key={item.id}> {item.name} <button onClick={() => handleDelete(item.id)}>Delete</button> </li> ))} </ul> ); }
Common event types and their TypeScript types:
tsx// Click events const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {}; // Form events const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); }; // Input change events const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { console.log(e.target.value); }; // Keyboard events const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { if (e.key === "Enter") { console.log("Enter pressed"); } }; // Focus events const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {};
Event propagation works the same as in the DOM. Events bubble up from child to parent:
tsxfunction Parent() { return ( <div onClick={() => console.log("Parent clicked")}> <button onClick={(e) => { e.stopPropagation(); // Prevents bubbling to parent console.log("Button clicked"); }}> Click me </button> </div> ); }
Common Pitfalls
- Calling the function instead of passing a reference — Writing
onClick={handleClick()}calls the function immediately during render. UseonClick={handleClick}to pass a reference, oronClick={() => handleClick(arg)}when you need to pass arguments. - Forgetting
e.preventDefault()on forms — Without it, the browser performs a full page reload on form submission. Always calle.preventDefault()inonSubmithandlers (or use the new formactionprop from React 19).
Best Practices
- Name handlers with the
handleprefix — UsehandleClick,handleSubmit,handleChangefor handler functions andonClick,onSubmit,onChangefor the props. This convention makes it clear what triggers what. - Use TypeScript event types — Always type your event parameters (
React.MouseEvent,React.ChangeEvent, etc.) to get autocomplete and catch errors at build time.
Summary
- React events use camelCase naming and accept function references, not strings.
- SyntheticEvent provides a consistent cross-browser API wrapping native DOM events.
- Use
e.preventDefault()to prevent default behavior ande.stopPropagation()to stop event bubbling.
Code Examples
function EventDemo() {
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log("Clicked!", e.currentTarget.textContent);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
console.log("Email:", formData.get("email"));
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Escape") e.currentTarget.blur();
};
return (
<form onSubmit={handleSubmit}>
<input
name="email"
type="email"
onChange={(e) => console.log(e.target.value)}
onKeyDown={handleKeyDown}
/>
<button onClick={handleClick}>Submit</button>
</form>
);
}