Skip to content

Props in React

Props (short for properties) are how parent components pass data and callbacks to children. They are read-only and flow one way down the tree. This lesson covers prop syntax, defaults, validation, and common patterns.

Props as Component Inputs

When you render <Avatar name="Ada" size={48} />, React passes an object { name: 'Ada', size: 48 } as the first argument to Avatar. The child uses those values to decide what to render.

Props must never be mutated inside the child. If a child needs to change data, it calls a callback prop provided by the parent, which updates state upstream and flows new props down.

function UserCard({ name, role, onEdit }) {
  return (
    <div>
      <h3>{name}</h3>
      <p>{role}</p>
      <button onClick={onEdit}>Edit</button>
    </div>
  );
}

<UserCard name="Ada Lovelace" role="Engineer" onEdit={() => openModal()} />

Functions can be props too — onEdit lets the child notify the parent without owning modal state.

Prop Patterns

// Destructuring with defaults
function Badge({ label = 'New', color = 'blue' }) { ... }

// Rest props (forward to DOM)
function Input({ label, ...inputProps }) {
  return <label>{label}<input {...inputProps} /></label>;
}

// Boolean shorthand
<Button disabled />  // same as disabled={true}
  • Destructure props in the function signature for readability.
  • Provide default values for optional props.
  • Use {...rest} to forward unknown props to native elements.
  • Boolean props can omit ={true} for brevity.

Props Cheat Sheet

Essential prop patterns in React.

Pattern Example
String prop <Title text="Hello" />
Expression prop <Counter initial={count} />
Function prop <List onSelect={handleSelect} />
Children prop <Card>content</Card>
Spread props <Input {...register('email')} />
Default value function X({ n = 0 })
Renamed prop function X({ className: cn })

Prop Drilling and When It Hurts

Prop drilling means passing props through many intermediate components that do not use them, only forwarding down. It is fine for shallow trees; for deep sharing, consider composition, context, or a state library.

  • Composition: pass JSX as children instead of props through layers.
  • Context: share theme, auth, or locale without drilling.
  • State libraries: Redux/Zustand for complex global client state.
  • Colocate state closer to where it is used when possible.

Validating Props

In JavaScript projects, PropTypes (prop-types package) offer runtime checks in development. In TypeScript projects, interfaces and types catch prop errors at compile time — the preferred approach for new apps.

interface ButtonProps {
  label: string;
  variant?: 'primary' | 'secondary';
  onClick: () => void;
}

function Button({ label, variant = 'primary', onClick }: ButtonProps) {
  return <button className={variant} onClick={onClick}>{label}</button>;
}

Common Mistakes

  • Mutating props or nested objects received from parents.
  • Passing too many individual props when a single config object is clearer.
  • Creating new object/function props inline on every render, breaking memoization.
  • Forgetting that children is also a prop.

Key Takeaways

  • Props are read-only inputs passed from parent to child.
  • Callbacks as props enable children to communicate upward.
  • Destructure, default, and validate props for maintainability.
  • Use context or state libraries when prop drilling becomes painful.

Pro Tip

If a component accepts more than ~5 props, consider grouping related ones into an object or splitting the component.