Question

Typing React onChange Event.target.value in TypeScript

typescriptreactjstypestypescript-typings

Question

In a React and TypeScript application, I am using this code:

onChange={(e) => {
  data.motto = (e.target as any).value;
}}

How can I type this correctly so I do not need to bypass the type system with any?

Here is the component setup:

export interface InputProps extends React.HTMLProps<Input> {
  // ...
}

export class Input extends React.Component<InputProps, {}> {
}

I tried adding something like target: { value: string }, but then I got this error:

Interface 'InputProps' incorrectly extends interface 'HTMLProps<Input>'.
Types of property 'target' are incompatible.
Type '{ value: string; }' is not assignable to type 'string'.

What is the proper TypeScript way to type an input onChange handler so event.target.value or event.currentTarget.value is safely available?

Short Answer

By the end of this page, you will understand how React types onChange events in TypeScript, why any is unnecessary here, and how to correctly type reusable input component props. You will also learn the difference between target and currentTarget, and how to write safer event handlers for form inputs.

Concept

In React with TypeScript, form events already have built-in types. You usually do not add a custom target property to your props interface. Instead, you type the event handler itself.

For an HTML <input>, the correct event type is usually:

React.ChangeEvent<HTMLInputElement>

That means TypeScript knows the event came from an HTMLInputElement, so properties like .value, .checked, and .name are available.

A common beginner mistake is trying to modify the props interface to redefine event internals such as target. That does not work because target is part of the event object, not your component's props shape.

Another important detail: in React, currentTarget is often safer to use than target.

  • target is the element that originally triggered the event.
  • currentTarget is the element the handler is attached to.

For typed form handlers, this is the common pattern:

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  console.log(e.currentTarget.value);
};

This matters in real programming because form handling is everywhere:

  • search boxes
  • login forms
  • settings pages
  • filters
  • admin dashboards
  • controlled inputs in React state

If you type events correctly, TypeScript can catch mistakes early and provide autocomplete for input-specific properties.

Mental Model

Think of a React event as a labeled package.

  • The event type tells you what kind of package it is.
  • The generic type like HTMLInputElement tells you which element sent it.
  • Once the label is correct, TypeScript knows what is inside.

So instead of saying, "Trust me, this package contains a value" with as any, you tell TypeScript, "This is a change event from an input element." Then TypeScript can safely allow value.

Another way to think about it:

  • props describe what your component accepts.
  • event describes what happened during user interaction.

Do not try to store event typing inside props fields like target. Type the event where it is used.

Take Quiz

Syntax and Examples

Basic typed onChange

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.currentTarget.value;
  console.log(value);
};

<input onChange={handleChange} />

Here, TypeScript knows:

  • this is a change event
  • it comes from an HTMLInputElement
  • value is a string

Inline handler

<input
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
    data.motto = e.currentTarget.value;
  }}
/>

Better reusable props typing

If you are building a custom Input component, use input element props rather than HTMLProps<Input> where Input is your React class.

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
}

class Input extends React.Component<InputProps> {
  render() {
    return <input {...this.props} />;
  }
}

This works because React.InputHTMLAttributes<HTMLInputElement> describes the props of a real HTML input element.

Example with state

import React from "react";

export default function ProfileForm() {
  const [motto, setMotto] = React.useState("");

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setMotto(e.currentTarget.value);
  };

  return <input value={motto} onChange={handleChange} />;
}

This is the most common React pattern: a controlled input tied to state.

Step by Step Execution

Consider this code:

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.currentTarget.value;
  console.log(value);
};

Step by step:

  1. The user types into the input.
  2. React fires an onChange event.
  3. The handler receives e.
  4. TypeScript sees that e is a React.ChangeEvent<HTMLInputElement>.
  5. Because the element type is HTMLInputElement, e.currentTarget.value is known to be a string.
  6. The string is stored in value.
  7. You can now assign it to state, an object field, or send it to validation logic.

Example in context:

function App() {
  const [name, setName] = React.useState("");

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const nextValue = e.currentTarget.value;
    setName(nextValue);
  };

  return <input value={name} onChange={handleChange} />;
}

Execution trace when the user types A:

  • Initial state: name = ""
  • User types A
  • onChange runs
  • e.currentTarget.value === "A"
  • setName("A")
  • React re-renders
  • Input now displays A from state

Real World Use Cases

Correctly typed input change handlers are used in many real applications:

Forms

const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setEmail(e.currentTarget.value);
};

Used in:

  • sign-up forms
  • password reset forms
  • checkout forms

Search and filtering

const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
  setQuery(e.currentTarget.value);
};

Used in:

  • product search
  • table filtering
  • admin dashboards

Validation

const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.currentTarget.value;
  setUsername(value);
  setTooShort(value.length < 3);
};

Used in:

  • live validation
  • character limits
  • required-field checks

Reusable UI components

A shared Input component often passes the typed onChange down to a real HTML input.

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label: string;
}
Take Quiz

Real Codebase Usage

In real projects, developers usually rely on built-in React prop types instead of redefining DOM event shapes manually.

Common pattern: reuse native input props

type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
  label?: string;
};

This gives your component support for standard props such as:

  • value
  • defaultValue
  • onChange
  • placeholder
  • disabled
  • name

Common pattern: typed handler passed as prop

interface InputProps {
  value: string;
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

This is useful when you want strict control over which props are allowed.

Guard clauses in handlers

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.currentTarget.value;
  if (value.length > 100) return;
  setMotto(value);
};

This prevents invalid state updates early.

Validation and transformation

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.currentTarget.value.trimStart();
  setMotto(value);
};

Controlled components

Most production React forms use controlled inputs:

<input value={motto} onChange={handleChange} />

This makes input data predictable and easier to validate, submit, and test.

Common Mistakes

1. Using any

Broken:

onChange={(e) => data.motto = (e.target as any).value}

Why it is a problem:

  • disables type safety
  • hides mistakes
  • loses autocomplete

Better:

onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
  data.motto = e.currentTarget.value;
}}

2. Typing the wrong thing

Broken:

interface InputProps extends React.HTMLProps<Input> {
  target: { value: string };
}

Why it is wrong:

  • target belongs to the event object, not the component props
  • Input here is your React component class, not the DOM <input> element

Better:

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
}

3. Using the component class instead of the DOM element type

Broken:

React.HTMLProps<Input>

Why it is wrong:

  • Input is a React component
  • HTML attribute types should use DOM element types like HTMLInputElement

Better:

React.InputHTMLAttributes<HTMLInputElement>

4. Confusing target and currentTarget

This may work sometimes:

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  console.log(e.target.value);
};

But this is often clearer and safer:

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  console.log(e.currentTarget.value);
};

5. Forgetting that different form elements have different types

Examples:

  • text input: HTMLInputElement
  • textarea: HTMLTextAreaElement
  • select: HTMLSelectElement

Correct examples:

const onTextAreaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
  console.log(e.currentTarget.value);
};

const onSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
  console.log(e.currentTarget.value);
};

Comparisons

ConceptBest UseExample Type
React.ChangeEvent<HTMLInputElement>Typing input change eventsText inputs, checkboxes
React.ChangeEvent<HTMLTextAreaElement>Typing textarea changesMulti-line text
React.ChangeEvent<HTMLSelectElement>Typing select changesDropdowns
React.InputHTMLAttributes<HTMLInputElement>Reusing native input props in a custom componentWrapper input component
React.HTMLProps<HTMLInputElement>Broader HTML props typingLess specific alternative

target vs currentTarget

PropertyMeaningRecommended for typed handlers?
targetThe original element that triggered the eventSometimes
currentTargetThe element the handler is attached toUsually yes

any vs proper typing

ApproachResult
(e.target as any).valueWorks, but removes safety
e: React.ChangeEvent<HTMLInputElement>Safe, explicit, autocomplete-friendly
Take Quiz

Cheat Sheet

// Typed input change event
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.currentTarget.value;
};
// Custom input props
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
}
// Explicit onChange prop typing
interface InputProps {
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
  value?: string;
}

Rules

  • Use React.ChangeEvent<HTMLInputElement> for <input> changes.
  • Prefer e.currentTarget.value for safer typing.
  • Do not redefine target in component props.
  • Use DOM element types like HTMLInputElement, not your React component class.
  • For reusable input wrappers, prefer React.InputHTMLAttributes<HTMLInputElement>.

Common element types

React.ChangeEvent<HTMLInputElement>
React.ChangeEvent<HTMLTextAreaElement>
React.ChangeEvent<HTMLSelectElement>

Good default pattern

type Props = React.InputHTMLAttributes<HTMLInputElement>;

Avoid

(e.target as any).value
React.HTMLProps<Input>

FAQ

How do I type onChange for a React input in TypeScript?

Use:

(e: React.ChangeEvent<HTMLInputElement>) => { ... }

Should I use target.value or currentTarget.value?

currentTarget.value is usually the safer and clearer choice in typed React handlers.

Why is React.HTMLProps<Input> wrong here?

Because Input is your React component class, not the DOM element type. For a real input element, use HTMLInputElement.

What is the correct props type for a reusable input component?

Usually:

React.InputHTMLAttributes<HTMLInputElement>

Can I still use an inline onChange handler?

Yes.

<input onChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.currentTarget.value)} />

How do I type a textarea change event?

Use:

React.ChangeEvent<HTMLTextAreaElement>

Why should I avoid any here?

Because any turns off type checking and hides mistakes that TypeScript could catch for you.

Take Quiz

Mini Project

Description

Build a small profile form with a reusable typed input component. This project demonstrates how to pass native input props through a custom React component and how to safely read value from a typed onChange event without using any.

Goal

Create a reusable Input component and use it in a form to edit a motto field with fully typed React change events.

Requirements

  • Create a reusable Input component that accepts standard HTML input props.
  • Type the onChange handler correctly for an HTML input element.
  • Store the input value in React state.
  • Display the current motto below the input.
  • Do not use any or manual target overrides.
Take Quiz

Keep learning

Related questions

View more