TypeScript with React
TypeScript catches the most common React bugs — a missing prop, a misspelled field from the API, a wrong event type — while you type. It is now the default for professional React projects.
On this page · 6 min · Advanced
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Advanced
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Type component props and children
- Type useState, useRef and event handlers
- Write a generic component
- Type API responses
Explanation#
Start with npm create vite@latest app -- --template react-ts. Component files use the .tsx extension.
Describe props with a type or interface: type ButtonProps = { label: string; onClick: () => void; variant?: "primary" | "ghost" }. Optional props use ?; children is React.ReactNode.
useState infers simple types; give a type when the initial value is empty: useState<User | null>(null), useState<Product[]>([]).
Events: React.ChangeEvent<HTMLInputElement>, React.FormEvent<HTMLFormElement>, React.MouseEvent<HTMLButtonElement>. Refs: useRef<HTMLInputElement>(null).
Generic components work with any item type: function List<T>({ items, render }: { items: T[]; render: (item: T) => React.ReactNode }).
Type API data at the boundary: const data = (await res.json()) as Product[] — and validate untrusted data with a schema library (Zod) when it matters.
Syntax#
type Props = { title: string; count?: number; children?: React.ReactNode };
function Panel({ title, count = 0, children }: Props) {
const [open, setOpen] = useState<boolean>(true);
return <section><h2>{title} ({count})</h2>{open && children}</section>;
}
Code example: Typed props, state, events and a generic list#
import { useState, type ChangeEvent, type FormEvent, type ReactNode } from 'react';
type Product = { id: number; name: string; price: number; category: 'Stationery' | 'Bags' };
type ListProps<T> = {
items: T[];
getKey: (item: T) => string | number;
render: (item: T) => ReactNode;
empty?: string;
};
function List<T>({ items, getKey, render, empty = 'Nothing to show' }: ListProps<T>) {
if (items.length === 0) return <p>{empty}</p>;
return <ul>{items.map(item => <li key={getKey(item)}>{render(item)}</li>)}</ul>;
}
type PickerProps = { products: Product[]; onPick: (product: Product) => void };
function ProductPicker({ products, onPick }: PickerProps) {
const [query, setQuery] = useState(''); // inferred as string
const [picked, setPicked] = useState<Product | null>(null); // explicit: starts null
const handleChange = (e: ChangeEvent<HTMLInputElement>) => setQuery(e.target.value);
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const match = products.find(p => p.name.toLowerCase() === query.toLowerCase());
if (match) { setPicked(match); onPick(match); }
};
const visible = products.filter(p => p.name.toLowerCase().includes(query.toLowerCase()));
return (
<form onSubmit={handleSubmit}>
<input value={query} onChange={handleChange} aria-label="Product" />
<List items={visible} getKey={p => p.id} render={p => `${p.name} — ₹${p.price}`} />
{picked && <p>Picked: {picked.name} ({picked.category})</p>}
</form>
);
}
export default function App() {
const products: Product[] = [
{ id: 1, name: 'Pen', price: 20, category: 'Stationery' },
{ id: 2, name: 'Backpack', price: 1200, category: 'Bags' },
];
return <ProductPicker products={products} onPick={p => console.log('picked', p.id)} />;
}
Renders an input and the list “Pen — ₹20”, “Backpack — ₹1200”. Typing “pen” and pressing Enter shows “Picked: Pen (Stationery)”. In the editor, writing category: "Toys", forgetting the onPick prop, or calling p.prize all show TypeScript errors before the code runs.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- Product describes the data shape; category is restricted to two literal values.
- List<T> is generic: TypeScript infers T = Product from items, so render receives a typed product.
- useState<Product | null>(null) allows the state to hold a product later.
- Event handler parameters use React’s event types, so e.target.value is known to be a string.
- Props types make required props (products, onPick) mandatory at every call site.
Real-world use case#
When the backend renames price to unitPrice, the team updates the Product type once; the compiler then lists every component that still reads .price, so nothing is missed.
Common mistakes#
TypeScript infers the type null, so setUser(user) is an error. Write useState<User | null>(null).
any turns checking off exactly where bugs hide. Write the type once and reuse it.
It is allowed, but plain functions with typed props are simpler and handle generics better.
Best practices#
- Start with the react-ts template and strict mode
- Type props with type aliases or interfaces
- Type API data in one shared file
- Use generics for reusable list/table components
Remember this#
Props: type Props = { ... }; function C({ ... }: Props).
Empty initial state needs a type: useState<User | null>(null).
Events: ChangeEvent<HTMLInputElement>, FormEvent<HTMLFormElement>.
Practice exercise#
Your task: Type a Button component with props label (string), onClick (no arguments, returns nothing), optional disabled (boolean) and variant ("primary" | "danger", default "primary").
Need a hint?
Show solution
type ButtonProps = {
label: string;
onClick: () => void;
disabled?: boolean;
variant?: 'primary' | 'danger';
};
function Button({ label, onClick, disabled = false, variant = 'primary' }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled} style={{ color: variant === 'danger' ? 'red' : 'blue' }}>
{label}
</button>
);
}
export default function Demo() {
return <Button label="Delete" variant="danger" onClick={() => console.log('deleted')} />;
}
Mini quiz#
How do you type state that starts as null and later holds a User?
Want more? Practise all React MCQs · React interview questions
My notes#
Saved in this browser.