How to type events in React with TypeScript
Managing events in React with TypeScript is essential for building scalable and error-free applications.
As the creator of CoreUI, I’ve spent over 25 years in software development, and I’ve implemented thousands of event handlers across our React component libraries.
The most efficient and modern solution is to use React’s built-in synthetic event types combined with HTML element generics.
This approach provides full type safety, ensuring that you access only the properties that actually exist on the triggering element.
Use React’s generic event types such as React.ChangeEvent<HTMLInputElement> or React.MouseEvent<HTMLButtonElement> to define your event handlers.
Typing Button Click Events
Handling clicks on buttons is the most common task in React. By using React.MouseEvent, you get access to mouse-specific properties like clientX and clientY, while the generic <HTMLButtonElement> tells TypeScript which element is dispatching the event.
import React from 'react'
import { CButton } from '@coreui/react'
const ClickExample = () => {
// We define the handler with the specific MouseEvent type
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
// TypeScript knows event.currentTarget is an HTMLButtonElement
console.log('Button clicked:', event.currentTarget.name)
}
return (
<CButton
name='action-button'
onClick={handleClick}
color='primary'
>
Click Me
</CButton>
)
}
export default ClickExample
In this example, React.MouseEvent<HTMLButtonElement> ensures that event.currentTarget is correctly typed. This is particularly useful when working with CoreUI Buttons where you might need to access custom attributes or names. By explicitly typing the handler, you avoid using any and gain full IDE autocompletion for event properties.
Typing Input Change Events
When handling text inputs, you should use React.ChangeEvent. This type is specifically designed for elements that emit a change value, such as <input>, <textarea>, or <select>. The generic type specifies the DOM element.
import React, { useState } from 'react'
import { CFormInput } from '@coreui/react'
const InputExample = () => {
const [value, setValue] = useState('')
// ChangeEvent is used for input fields
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newValue = event.currentTarget.value
setValue(newValue)
}
return (
<CFormInput
type='text'
value={value}
onChange={handleChange}
placeholder='Enter your name'
/>
)
}
The React.ChangeEvent<HTMLInputElement> type ensures that event.currentTarget.value is recognized as a string. If you are dealing with numeric inputs, you might need to convert a string to a number in JavaScript before saving it to your state. This pattern is fundamental when building forms with CoreUI Input components.
Typing Form Submit Events
Typing form submissions is critical to prevent the default browser behavior safely. The React.FormEvent type is the correct choice here, usually paired with HTMLFormElement.
import React from 'react'
import { CForm, CButton } from '@coreui/react'
const FormExample = () => {
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
// Prevent default form submission behavior
event.preventDefault()
const formData = new FormData(event.currentTarget)
const email = formData.get('email') as string
if (email) {
console.log('Form submitted with:', email)
}
}
return (
<CForm onSubmit={handleSubmit}>
<input name='email' type='email' required />
<CButton type='submit' color='success'>
Submit
</CButton>
</CForm>
)
}
Using React.FormEvent<HTMLFormElement> allows you to call event.preventDefault() without TypeScript complaints. It also types event.currentTarget as the form itself, which is useful for extracting data via the FormData API. Before processing, you might want to check if a string is empty in JavaScript to validate your inputs.
Typing Select and Textarea Events
While ChangeEvent is used for both, the generic type must match the specific HTML element to provide accurate property access. If you use a select component, you should use HTMLSelectElement.
import React from 'react'
import { CFormSelect, CFormTextarea } from '@coreui/react'
const MixedFieldsExample = () => {
const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
console.log('Selected value:', event.target.value)
}
const handleTextareaChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
console.log('Textarea content:', event.target.value)
}
return (
<>
<CFormSelect onChange={handleSelectChange}>
<option value='1'>Option 1</option>
<option value='2'>Option 2</option>
</CFormSelect>
<CFormTextarea onChange={handleTextareaChange} />
</>
)
}
By distinguishing between HTMLSelectElement and HTMLTextAreaElement, you ensure that element-specific properties are available. For instance, CoreUI Select components rely on these types to provide a consistent API for developers using TypeScript.
Typing Keyboard Events
Keyboard events like onKeyDown or onKeyUp require the React.KeyboardEvent type. This provides access to properties like key, code, shiftKey, and ctrlKey.
import React from 'react'
const KeyboardExample = () => {
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
console.log('Enter key pressed!')
}
}
return (
<input
type='text'
onKeyDown={handleKeyDown}
placeholder='Press Enter...'
/>
)
}
React.KeyboardEvent<HTMLInputElement> allows you to safely check event.key. This is highly useful for accessibility and custom shortcuts within your UI components. It ensures that the event listener is contextually aware of the input element it is attached to.
Typing Focus and Blur Events
Focus events are typed using React.FocusEvent. These are useful for validation or showing/hiding UI elements like tooltips or dropdowns when an element gains or loses focus.
import React from 'react'
const FocusExample = () => {
const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
console.log('Input focused:', event.target.id)
}
const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {
console.log('Input lost focus')
}
return (
<input
id='search-input'
onFocus={handleFocus}
onBlur={handleBlur}
/>
)
}
With React.FocusEvent<HTMLInputElement>, you can access the relatedTarget property, which tells you which element received focus after the current one lost it. This is a common requirement when building complex interactive layouts or navigation systems.
Best Practice Note:
Always prefer specific event types over the generic React.SyntheticEvent. While SyntheticEvent works as a base class, it lacks the specific properties of more specialized events like MouseEvent or ChangeEvent.
At CoreUI, we use these explicit types in all our internal handlers to guarantee that our components are robust and easy to extend by other developers. Using the currentTarget property is also generally safer than target in TypeScript, as currentTarget is guaranteed to be the element the listener is attached to.



