React Typing Props & State β Write Safer Components with TypeScript (2025 Guide)
Introduction β Why Type Props & State in React?
In React.js, props and state are essential for component behavior and rendering. By using TypeScript, you bring type safety to these data structures, helping:
- Prevent runtime bugs
- Enable powerful IntelliSense/autocomplete
- Document component APIs with explicit typings
Whether you’re working with form data, UI toggles, or complex nested props, typing them ensures your components are predictable, maintainable, and scalable.
In this guide, youβll learn:
- How to type props and state in functional components
- Use optional, union, and nested props
- Type
useState, event handlers, and object states - Best practices for reusable prop types
1. Typing Props in Functional Components
Example: Basic Props
type GreetingProps = {
name: string;
age?: number; // optional
};
const Greeting = ({ name, age }: GreetingProps) => (
<p>Hello, {name}! {age && `(Age: ${age})`}</p>
);
GreetingProps defines what data the component expects
Helps catch prop misuse early
With React.FC (FunctionComponent)
const Greeting: React.FC<GreetingProps> = ({ name, age }) => (
<p>Hello, {name}! {age}</p>
);
React.FC automatically types children but adds overheadβprefer plain function types for performance-critical code.
2. Typing Component State with useState
Simple State:
const [count, setCount] = useState<number>(0);
Object State:
type FormState = {
username: string;
email: string;
};
const [form, setForm] = useState<FormState>({
username: '',
email: '',
});
Keep object state type-safe
Type useState<T>() to define the structure
3. Optional & Default Props
type ButtonProps = {
label: string;
variant?: 'primary' | 'secondary';
};
const Button = ({ label, variant = 'primary' }: ButtonProps) => (
<button className={variant}>{label}</button>
);
Use ? for optional props
Provide default values in the function body
4. Event Handlers Typing
Input Change Event:
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
Button Click Event:
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('Clicked!', e.currentTarget);
};
Use React.Event, React.ChangeEvent, React.MouseEvent, etc.
5. Typing Props with Children
type CardProps = {
title: string;
children: React.ReactNode;
};
const Card = ({ title, children }: CardProps) => (
<div className="card">
<h2>{title}</h2>
{children}
</div>
);
React.ReactNode supports text, elements, fragments, arrays, etc.
6. Reusable Prop Patterns with Union & Enums
Union Props:
type AlertProps =
| { type: 'success'; message: string }
| { type: 'error'; errorCode: number };
const Alert = (props: AlertProps) => {
if (props.type === 'success') return <p>{props.message}</p>;
return <p>Error Code: {props.errorCode}</p>;
};
Enum Props:
enum Variant {
Primary = 'primary',
Secondary = 'secondary',
}
type ButtonProps = {
label: string;
variant: Variant;
};
Use union types for conditional rendering
Use enums when values are shared across components
7. Typing Props for Custom Hooks
type ToggleHook = [boolean, () => void];
function useToggle(initial: boolean): ToggleHook {
const [on, setOn] = useState(initial);
const toggle = () => setOn(prev => !prev);
return [on, toggle];
}
Clear return types enhance reusability
Useful when sharing state logic across multiple components
Best Practices
| Practice | Why It Helps |
|---|---|
Use type or interface | Makes components self-documenting |
Always annotate useState for non-primitives | Prevents inference issues |
| Use union types for conditional props | Enforces stricter usage |
| Keep prop types small & reusable | Improves readability and reuse |
Avoid any unless absolutely needed | Maintain type safety and clarity |
Summary β Recap & Next Steps
Typing props and state in React with TypeScript helps you build stable, self-documenting components. You gain better autocomplete, validation, and early error detectionβall of which help scale your project with confidence.
Key Takeaways:
- Use
type/interfaceto define prop and state shapes - Annotate
useStatewhen dealing with objects, nulls, or arrays - Use
React.ChangeEvent,React.MouseEventfor event handlers - Pass children using
React.ReactNode - Use union types or enums for component variants
Real-World Relevance:
Used in large TypeScript codebases like Shopify Polaris, Radix UI, and Microsoft Fabric to deliver type-safe and reusable components.
FAQ Section
What’s the difference between type and interface?
Both can define shapes. Prefer type for union/intersections and interface for extending object contracts.
Do I need to type state if it’s a number or string?
Not always. TypeScript can infer simple types, but use explicit types for objects or when initializing to null.
Can I type default props?
Yes. Provide defaults in the function body and mark them as optional (prop?: string).
How do I avoid repeating prop types in multiple files?
Export shared types into a /types folder and import as needed:
import { ButtonProps } from '@/types/button';
How to type children correctly in React?
Use React.ReactNode or ReactElement depending on flexibility:
type Props = { children: React.ReactNode };
Share Now :
