TS in React

Typing Components, Props, Hooks, and Events

TS in React

React with TypeScript is the standard frontend stack in 2026. Here are the patterns you'll use every day.

5 min read Level 2/5 #typescript#react#hooks
What you'll learn
  • Type function components and their props
  • Type useState, useRef, and useReducer
  • Type events and DOM refs

React + TS is the standard frontend stack. The patterns are small in number — once you know them, you know them.

Function Components

type ButtonProps = {
  label: string;
  onClick: () => void;
  disabled?: boolean;
};

function Button({ label, onClick, disabled }: ButtonProps) {
  return (
    <button onClick={onClick} disabled={disabled}>
      {label}
    </button>
  );
}

A plain function. TS infers the return type as JSX. You almost never need React.FC (it has its own quirks — skip it).

children

import { type ReactNode } from "react";

type CardProps = {
  title: string;
  children: ReactNode;
};

function Card({ title, children }: CardProps) {
  return (
    <section>
      <h2>{title}</h2>
      {children}
    </section>
  );
}

ReactNode accepts anything renderable — strings, numbers, elements, arrays, fragments.

Hooks

import { useState, useRef, useReducer } from "react";

const [count, setCount] = useState(0);          // count: number
const [user, setUser]   = useState<User | null>(null);

const ref = useRef<HTMLInputElement>(null);     // ref.current?: HTMLInputElement
ref.current?.focus();

useState infers from the initial value. When the initial value is null (but the state can hold other types), provide a type argument.

useRef<T>(null) returns a RefObject<T | null> — TS forces you to handle the “not mounted yet” case.

Events

import { type ChangeEvent, type FormEvent } from "react";

function Form() {
  function onChange(e: ChangeEvent<HTMLInputElement>) {
    console.log(e.target.value);
  }
  function onSubmit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
  }
  return (
    <form onSubmit={onSubmit}>
      <input onChange={onChange} />
    </form>
  );
}

Event types are parameterized by the element. ChangeEvent<HTMLInputElement>, MouseEvent<HTMLButtonElement>, etc.

If you write the handler inline, you don’t need to type the event:

<input onChange={e => console.log(e.target.value)} />
// e is inferred as ChangeEvent<HTMLInputElement>

Generic Components

type ListProps<T> = {
  items: T[];
  renderItem: (item: T) => ReactNode;
};

function List<T>({ items, renderItem }: ListProps<T>) {
  return <ul>{items.map((it, i) => <li key={i}>{renderItem(it)}</li>)}</ul>;
}

<List items={[1, 2, 3]} renderItem={n => n * 2} />
<List items={users}    renderItem={u => u.name} />

Generic components let you reuse rendering scaffolding without losing type info.

Up Next

TS in Node — the server-side variant.

TS in Node →