Skip to content

TypeScript with React

A React component is a function, so you type it by typing its props. Define a type (or interface) for the props and annotate the parameter:

type ButtonProps = {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary'; // optional, a literal union
};
function Button({ label, onClick, variant = 'primary' }: ButtonProps) {
return <button className={variant} onClick={onClick}>{label}</button>;
}

Prefer typing the props object directly (as above) over the older React.FCReact.FC has fallen out of favor (it complicates generics and children typing). Just annotate the destructured props parameter.

When a component wraps other content, type children as React.ReactNode — the broadest “anything renderable” type:

type CardProps = {
title: string;
children: React.ReactNode; // JSX, strings, numbers, arrays, null…
};
function Card({ title, children }: CardProps) {
return <section><h2>{title}</h2>{children}</section>;
}

Other types you’ll reach for: React.ReactNode (renderable content), React.ComponentProps<'button'> (steal a host element’s props), and React.CSSProperties (a style object).

Inference handles most hooks, but supply a type argument when the initial value doesn’t pin it down:

const [count, setCount] = useState(0); // inferred: number
const [user, setUser] = useState<User | null>(null); // needs the annotation
const inputRef = useRef<HTMLInputElement>(null); // ref to a DOM node
const timer = useRef<number | undefined>(undefined); // ref to a mutable value

Event handlers get typed event objects. Let contextual typing infer them inline where possible; annotate when you extract the handler:

function onChange(e: React.ChangeEvent<HTMLInputElement>) {
console.log(e.target.value);
}
<button onClick={(e) => e.preventDefault()}>Go</button> // e inferred as React.MouseEvent

A concrete currency win: in React 19, ref is a regular prop for function components, so you no longer need forwardRef and its awkward generics. You type ref right alongside the other props:

// React 19 — ref is just a prop.
type InputProps = {
placeholder?: string;
ref?: React.Ref<HTMLInputElement>;
};
function TextInput({ placeholder, ref }: InputProps) {
return <input placeholder={placeholder} ref={ref} />;
}

forwardRef still works and older code using React.forwardRef<HTMLInputElement, Props> is fine, but for new components in React 19 you skip it entirely — one fewer wrapper and no generic gymnastics.

How should you type a component’s props in modern React + TypeScript?
What type do you use for a component’s `children`?
When must you pass a type argument to useState?
How do you handle a ref on a function component in React 19?