Back to React tutorials
Basic13 min read

Components & Props

Design reusable components with clear prop contracts, composition, and children patterns.

Props as Component API

Props are read-only inputs to components. Treat them as a public API — changes are breaking for consumers. Keep prop surfaces minimal and name them consistently across your design system.

Destructure props in the function signature for clarity. Provide default values via default parameters or defaultProps (legacy). Document required vs optional props with TypeScript interfaces.

  • Props are immutable for child
  • Minimal, stable prop APIs
  • TypeScript interfaces document contracts
function Avatar({ src, alt, size = 40 }: AvatarProps) {
  return <img src={src} alt={alt} width={size} height={size} />;
}

Composition Over Configuration

Prefer composable children and slots over dozens of boolean props (`showHeader`, `showFooter`, `showSidebar`). Pass JSX as children or render props when consumers need flexible layout control.

Compound components — parent + child subcomponents sharing context — model complex UI like tabs, accordions, and menus without prop drilling configuration through every layer.

  • Children for flexible layout
  • Avoid boolean prop explosion
  • Compound components for complex UI
<Card>
  <Card.Header>Title</Card.Header>
  <Card.Body>Content</Card.Body>
</Card>

Spreading and Rest Props

Spread remaining props onto native elements to support pass-through attributes: `function Input({ label, ...rest }) { return <input {...rest} /> }`. This enables aria attributes and event handlers without listing every HTML attribute.

Exclude props that should not reach the DOM (internal flags) before spreading. Type rest props with `ComponentPropsWithoutRef` utilities for correct HTML attribute typing.

  • Spread rest onto native elements
  • Strip internal props before spread
  • Type with ComponentPropsWithoutRef

Controlled vs Presentational

Presentational components receive data and callbacks, containing no data fetching or global state. Container components (or hooks) fetch data and pass props down. This separation improves testability — presentational components render from props alone.

Modern code often uses custom hooks instead of container components, colocating data logic with UI while keeping the hook testable independently.

  • Presentational: UI from props
  • Containers/hooks: data and logic
  • Hooks replace many container components

Prop Validation and Defaults

TypeScript catches most prop errors at compile time. For JavaScript codebases, PropTypes still validate at runtime in development. Default values should match the expected type — undefined optional props need null checks or defaults.

Avoid passing new object or function literals inline to memoized children on every render — they break referential equality. Stabilize callbacks with useCallback when passing to optimized children.

  • TypeScript for compile-time validation
  • Stable references for memoized children
  • Sensible defaults for optional props

Get In Touch


Ready to discuss your next project? Drop me a message.