🟦 React with TypeScript
Estimated reading: 4 minutes 291 views

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

PracticeWhy It Helps
Use type or interfaceMakes components self-documenting
Always annotate useState for non-primitivesPrevents inference issues
Use union types for conditional propsEnforces stricter usage
Keep prop types small & reusableImproves readability and reuse
Avoid any unless absolutely neededMaintain 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/interface to define prop and state shapes
  • Annotate useState when dealing with objects, nulls, or arrays
  • Use React.ChangeEvent, React.MouseEvent for 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 :
Share

🟦 React Typing Props & State

Or Copy Link

CONTENTS
Scroll to Top