Skip to content

Forms and Events

If performance is not an issue (and it usually isn't!), inlining handlers is easiest as you can just use type inference and contextual typing:

const el = (
  <button
    onClick={(event) => {
      /* event will be correctly typed automatically! */
    }}
  />
);

But if you need to define your event handler separately, IDE tooling really comes in handy here, as the @type definitions come with a wealth of typing. Type what you are looking for and usually the autocomplete will help you out. Here is what it looks like for an onChange for a form event:

type State = {
  text: string;
};
class App extends React.Component<Props, State> {
  state = {
    text: "",
  };

  // typing on RIGHT hand side of =
  onChange = (e: React.FormEvent<HTMLInputElement>): void => {
    this.setState({ text: e.currentTarget.value });
  };
  render() {
    return (
      <div>
        <input type="text" value={this.state.text} onChange={this.onChange} />
      </div>
    );
  }
}

View in the TypeScript Playground

Instead of typing the arguments and return values with React.FormEvent<> and void, you may alternatively apply types to the event handler itself (contributed by @TomasHubelbauer):

  // typing on LEFT hand side of =
  onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    this.setState({text: e.currentTarget.value})
  }
Why two ways to do the same thing? The first method uses an inferred method signature `(e: React.FormEvent): void` and the second method enforces a type of the delegate provided by `@types/react`. So `React.ChangeEventHandler<>` is simply a "blessed" typing by `@types/react`, whereas you can think of the inferred method as more... _artisanally hand-rolled_. Either way it's a good pattern to know. [See our Github PR for more](https://github.com/typescript-cheatsheets/react/pull/24).

Typing onSubmit, with Uncontrolled components in a Form

If you don't quite care about the type of the event, you can just use React.SyntheticEvent. If your target form has custom named inputs that you'd like to access, you can use a type assertion:

<form
  ref={formRef}
  onSubmit={(e: React.SyntheticEvent) => {
    e.preventDefault();
    const target = e.target as typeof e.target & {
      email: { value: string };
      password: { value: string };
    };
    const email = target.email.value; // typechecks!
    const password = target.password.value; // typechecks!
    // etc...
  }}
>
  <div>
    <label>
      Email:
      <input type="email" name="email" />
    </label>
  </div>
  <div>
    <label>
      Password:
      <input type="password" name="password" />
    </label>
  </div>
  <div>
    <input type="submit" value="Log in" />
  </div>
</form>

View in the TypeScript Playground

Of course, if you're making any sort of significant form, you should use Formik or React Hook Form, which are written in TypeScript.

List of event types

Event Type Description
AnimationEvent CSS Animations.
ChangeEvent Changing the value of <input>, <select> and <textarea> element.
ClipboardEvent Using copy, paste and cut events.
CompositionEvent Events that occur due to the user indirectly entering text (e.g. depending on Browser and PC setup, a popup window may appear with additional characters if you e.g. want to type Japanese on a US Keyboard)
DragEvent Drag and drop interaction with a pointer device (e.g. mouse).
FocusEvent Event that occurs when elements gets or loses focus.
FormEvent Event that occurs whenever a form or form element gets/loses focus, a form element value is changed or the form is submitted.
InvalidEvent Fired when validity restrictions of an input fails (e.g <input type="number" max="10"> and someone would insert number 20).
KeyboardEvent User interaction with the keyboard. Each event describes a single key interaction.
MouseEvent Events that occur due to the user interacting with a pointing device (e.g. mouse)
PointerEvent Events that occur due to user interaction with a variety pointing of devices such as mouse, pen/stylus, a touchscreen and which also supports multi-touch. Unless you develop for older browsers (IE10 or Safari 12), pointer events are recommended. Extends UIEvent.
TouchEvent Events that occur due to the user interacting with a touch device. Extends UIEvent.
TransitionEvent CSS Transition. Not fully browser supported. Extends UIEvent
UIEvent Base Event for Mouse, Touch and Pointer events.
WheelEvent Scrolling on a mouse wheel or similar input device. (Note: wheel event should not be confused with the scroll event)
SyntheticEvent The base event for all above events. Should be used when unsure about event type
What about InputEvent? You've probably noticed that there is no `InputEvent`. This is because it is not supported by Typescript as the event itself has no fully browser support and may behave differently in different browsers. You can use `KeyboardEvent` instead. Sources: - https://github.com/microsoft/TypeScript/issues/29441 - https://developer.mozilla.org/en-US/docs/Web/API/InputEvent - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event