TypeScript React Events — Complete Guide
In this tutorial, you will learn about TypeScript React Events. We cover key concepts, practical examples, and best practices to help you master this topic.
React event handling with TypeScript gives you typed event objects, preventing access to non-existent properties and ensuring your event handlers only receive the events they can handle.
What You'll Learn
- Typed event handler functions
- ChangeEvent, MouseEvent, KeyboardEvent, FormEvent
- Form handling with typed inputs
- Custom event patterns
Why It Matters
Event objects in React are synthetic wrappers around native events. Without types, you might access e.target.value on a button click (buttons don't have value). TypeScript catches these mistakes at compile time.
Real-World Use
The Doda Browser's settings page has complex forms with typed event handlers. An input change handler typed as ChangeEvent<HTMLInputElement> guarantees access to e.target.value, while a form submit handler typed as FormEvent<HTMLFormElement> ensures e.preventDefault() is available.
Learning Path
flowchart LR A[React Hooks] --> B[React Events] B --> C[React Context] B --> D[You Are Here] C --> E[State Management] E --> F[React Advanced]
Basic Event Handler Types
import { ChangeEvent, FormEvent, MouseEvent, KeyboardEvent } from 'react';
Mouse Events
function handleClick(e: MouseEvent<HTMLButtonElement>): void {
console.log(e.clientX, e.clientY); // Mouse coordinates
// e.target.value; // Error — buttons don't have value
}
<button onClick={handleClick}>Click me</button>
Change Events
function handleInputChange(e: ChangeEvent<HTMLInputElement>): void {
console.log(e.target.value); // string
}
function handleSelectChange(e: ChangeEvent<HTMLSelectElement>): void {
console.log(e.target.value); // string
}
<input type="text" onChange={handleInputChange} />
<select onChange={handleSelectChange}>
<option value="a">A</option>
</select>
Form Events
function handleSubmit(e: FormEvent<HTMLFormElement>): void {
e.preventDefault();
const formData = new FormData(e.currentTarget);
console.log(Object.fromEntries(formData));
}
<form onSubmit={handleSubmit}>
<input name="email" type="email" />
<button type="submit">Submit</button>
</form>
Keyboard Events
function handleKeyDown(e: KeyboardEvent<HTMLInputElement>): void {
if (e.key === 'Enter') {
console.log('Submitted via Enter');
}
if (e.key === 'Escape') {
console.log('Cancelled');
}
}
<input onKeyDown={handleKeyDown} />
Event Handler Type Syntax
Two ways to type inline event handlers:
// Option 1: Inline parameter type
<input onChange={(e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
}} />
// Option 2: Separated function
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
<input onChange={handleChange} />
Typed Forms
interface FormData {
email: string;
password: string;
remember: boolean;
}
function LoginForm() {
const [formData, setFormData] = useState<FormData>({
email: '',
password: '',
remember: false,
});
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}));
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('Login:', formData.email, formData.password);
};
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" value={formData.email} onChange={handleChange} />
<input name="password" type="password" value={formData.password} onChange={handleChange} />
<input name="remember" type="checkbox" checked={formData.remember} onChange={handleChange} />
<button type="submit">Login</button>
</form>
);
}
Custom Events
For event emitters or custom components:
interface TabChangeEvent {
tabId: string;
previousTab: string;
}
interface TabsProps {
tabs: { id: string; label: string }[];
activeTab: string;
onTabChange: (event: TabChangeEvent) => void;
}
function Tabs({ tabs, activeTab, onTabChange }: TabsProps) {
return (
<div className="tabs">
{tabs.map(tab => (
<button
key={tab.id}
className={tab.id === activeTab ? 'active' : ''}
onClick={() => onTabChange({
tabId: tab.id,
previousTab: activeTab,
})}
>
{tab.label}
</button>
))}
</div>
);
}
// Usage
<Tabs
tabs={[{ id: 'scan', label: 'Scan' }, { id: 'results', label: 'Results' }]}
activeTab="scan"
onTabChange={(e) => console.log(`Switched from ${e.previousTab} to ${e.tabId}`)}
/>
Event Type Reference
// Common event types
MouseEvent<HTMLElement> // onClick, onMouseEnter, onMouseLeave
ChangeEvent<HTMLInputElement> // onChange for inputs, selects, textareas
FormEvent<HTMLFormElement> // onSubmit, onReset
KeyboardEvent<HTMLElement> // onKeyDown, onKeyUp, onKeyPress
FocusEvent<HTMLElement> // onFocus, onBlur
DragEvent<HTMLElement> // onDrag, onDrop
WheelEvent<HTMLElement> // onWheel
TouchEvent<HTMLElement> // onTouchStart, onTouchEnd
Common Mistakes
1. Using any for Event Type
// Bad — no autocompletion or safety
const handleChange = (e: any) => { setValue(e.target.value); };
// Good — typed
const handleChange = (e: ChangeEvent<HTMLInputElement>) => { setValue(e.target.value); };
2. Confusing target and currentTarget
e.target— the element that triggered the event (may be a child)e.currentTarget— the element the handler is attached to
3. Not Using e.preventDefault() in Form Submit
Forgetting it causes page reload and state loss.
4. Wrong Generic Parameter on Event
// Wrong — div doesn't have value
<div onClick={(e: MouseEvent<HTMLInputElement>) => {}} />
// Correct — button
<button onClick={(e: MouseEvent<HTMLButtonElement>) => {}} />
5. Not Handling Checkbox Inputs Correctly
Checkboxes use checked not value. Always check the type property.
Practice Questions
What is the type of a button click handler parameter?
MouseEvent<HTMLButtonElement>What is the difference between
e.targetande.currentTarget?targetis the element that triggered the event (may be a child).currentTargetis the element the handler is on.How do you handle a checkbox input in TypeScript? Check
e.target.type === 'checkbox'and usee.target.checked.What event type should you use for form submission?
FormEvent<HTMLFormElement>.
Challenge: Build a multi-step form with typed event handlers. Each step has different input types (text, email, select, checkbox). Use a discriminated union to track the current step and form data.
FAQ
Mini Project: Typed Search Form
interface SearchFormData {
query: string;
category: string;
dateFrom: string;
dateTo: string;
exactMatch: boolean;
}
function SearchForm() {
const [form, setForm] = useState<SearchFormData>({
query: '', category: '', dateFrom: '', dateTo: '', exactMatch: false,
});
const handleInput = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value, type } = e.target;
const checked = type === 'checkbox' ? (e.target as HTMLInputElement).checked : undefined;
setForm(prev => ({ ...prev, [name]: checked ?? value }));
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('Search:', form);
};
return (
<form onSubmit={handleSubmit}>
<input name="query" value={form.query} onChange={handleInput} placeholder="Search..." />
<select name="category" value={form.category} onChange={handleInput}>
<option value="">All</option>
<option value="malware">Malware</option>
<option value="phishing">Phishing</option>
</select>
<label>
<input name="exactMatch" type="checkbox" checked={form.exactMatch} onChange={handleInput} />
Exact match
</label>
<button type="submit">Search</button>
</form>
);
}
What's Next
Now explore React Context with TypeScript:
| Lesson | Description |
|---|---|
| {{< ref "/programming-languages/typescript/38-react-hooks" >}} | Review hooks |
| {{< ref "/programming-languages/typescript/40-react-context" >}} | Typed context and providers |
| {{< ref "/programming-languages/typescript/41-react-state-management" >}} | Zustand, Redux Toolkit |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro