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.
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 →