JavaScript in the browser already includes a series of HTML events to which JavaScript can listen and react. The most used ones are events like onclick
, or onchange
, for instance.
However, these events' implementation and behavior vary between browsers. React solves this issue using synthetic events, a wrapper around native events that works the same across all browsers.
When we add event handlers in React, we can't use the types that TypeScript includes in the DOM library because React adds a wrapper around these events. Instead, we need to use the type definitions that React provides.
In JSX, you can easily see the difference between native and synthetic events by looking at the attribute. Native attributes are written in lower-case (
onclick
), while synthetic events use camelCase (onClick
).
React event types
We can't use TypeScript's type definitions for events, but the good thing is that React has equivalent type definitions for all its synthetic events.
Let's say we want to add an event handler to the onChange
event of an input
element.
<input value={value} onChange={handleInputChange} />
The type of the handleInputChange
function needs to match up with React's type definitions. In our case, we need to use React's ChangeEvent
.
import React, { ChangeEvent } from 'react';
const App = () => {
const [value, setValue] = React.useState('');
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => { setValue(e.target.value);
};
return (
<>
<h1>Hi {value} 👋</h1>
<input value={value} onChange={handleInputChange} />
</>
);
};
All synthetic event types are generic, so we can specify the exact element that this event refers to (between the angle brackets <>
). TypeScript includes these definitions in the DOM lib, which is why you don't need to import them.
Alternative interfaces
Instead of applying the type to the argument of the function, you can also apply a type to the handler itself.
import React, { ChangeEventHandler, ChangeEvent } from 'react';
// This
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
// is the same as this
const handleInputChange = (e: ChangeEvent<HTMLInputElement>): void => {
Both ways of doing this are fine, what you choose to use in the end comes down to personal preference.
What if there is no matching type definition?
In case that there is no definition for the event handler you are trying to use, you can use React's SyntheticEvent
type.
All events build on top of this definition, so this should be compatible with all event handlers.
Tip: How to find type definitions for any event
Of course, you don't need to know all event types by heart; I only regularly use onClick
and onChange
events anyway.
When you're not sure what type you need to use, you can add the JSX attribute first (onChange
for example) and then jump to the definition in your editor.
To do this in VSCode, hold down CTRL or CMD and click on the attribute; other editors may have other shortcuts for this.
Alternatively, you can also search in React's type definition file on GitHub.
Conclusion
I hope that you now have a good understanding of how to use React's type definitions for synthetic events.
If you have any questions, please don't hesitate to send me an email, so I can improve the article with your feedback.