Bulletproof UI Components
Using React with plain JavaScript is like building a skyscraper without blueprints. If a parent component accidentally passes a string into a child component that expects an array, the app crashes at runtime. TypeScript eliminates these errors by enforcing strict types for Props, State, and Events before the code is ever run.
Typing Component Props
This is the absolute most common use of TypeScript in React. We use an interface to define exactly what props a component requires, and what data types they must be.
// 1. Define the Props interface
interface UserCardProps {
name: string;
age: number;
isAdmin?: boolean; // The '?' makes this prop optional
}
// 2. Apply the interface to the component's parameter destructing
export default function UserCard({ name, age, isAdmin = false }: UserCardProps) {
return (
<div className="card">
<h3>{name} ({age})</h3>
{isAdmin && <span>Admin Badge</span>}
</div>
);
}
Typing the 'children' Prop
If you build a wrapper component (like a Modal or Layout), it will receive children. The standard type for anything React can render (strings, numbers, JSX) is React.ReactNode.
import React from 'react';
interface ModalProps {
isOpen: boolean;
children: React.ReactNode;
}
export default function Modal({ isOpen, children }: ModalProps) {
if (!isOpen) return null;
return <div className="modal">{children}</div>;
}
Typing State (useState)
TypeScript can usually infer state types automatically (e.g., useState(0) is inferred as a number). But if the state starts as null or an empty array, you MUST use Generics to tell TypeScript what it will eventually hold.
interface User { id: string; name: string; }
export default function Profile() {
// TS doesn't know what this will hold. It assumes 'null' forever.
// const [user, setUser] = useState(null); // BAD
// We use Generics to say: "This will hold a User object, OR null"
const [user, setUser] = useState<User | null>(null); // GOOD
return <div>{user ? user.name : 'Loading...'}</div>;
}
Typing Events
When you pass an event object (e) to a handler, TS needs to know what kind of event it is. React provides specific types for Form events, Mouse events, and Keyboard events.
import React, { useState } from 'react';
export default function Search() {
const [query, setQuery] = useState("");
// React.ChangeEvent specifies that this event comes from an HTML Input Element
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
};
// React.FormEvent specifies this is a form submission
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log("Searching for:", query);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={query} onChange={handleChange} />
<button type="submit">Search</button>
</form>
);
}