renderpx
Theme: auto

HOCs vs Composition

Reuse logic or gating by wrapping components. HOCs (higher-order components) wrap a component and return a new one, often injecting props. Composition uses a wrapper component that renders children conditionally or provides context. Prefer composition for guards and layout; use HOCs when you need to augment a component type.

The problem I keep seeing

You need to add the same behavior to many components—auth check, theme, tracking. Two options: a higher-order component (HOC) that takes a component and returns a wrapped one, or a wrapper component that uses composition (e.g. AuthGuard that renders children only when authenticated). Both work; composition is usually simpler and avoids ref/displayName issues.

Naive approach

A HOC: withAuth(Page) wraps the component, runs the auth check, and injects user as a prop. Every page that needs auth is wrapped. Multiple HOCs stack and refs don’t pass through unless you use forwardRef in each layer.

tsx
Loading...

First improvement

Composition: a component that accepts children and renders them only when the condition is met (e.g. user is logged in). Use it once at layout or route level. No prop injection, no wrapping every page; the tree stays readable.

tsx
Loading...

Remaining issues

  • Injecting data: If children need user, use context: an AuthProvider that wraps the tree and a useUser() hook. No HOC needed.
  • When HOCs still make sense: When you’re augmenting a component type (e.g. Redux connect, or a library that expects a HOC). Use forwardRef and set displayName for debugging.

Production pattern

Prefer composition: guard components (AuthGuard, FeatureGate) that render children or redirect. Use context + hooks to provide data (e.g. useUser()) so any descendant can consume it without a HOC. Reserve HOCs for cases where you must wrap a component type (e.g. legacy Redux, or a third-party API that expects a HOC); then use forwardRef and preserve displayName.

tsx
Loading...

When I use this

  • Composition: Auth gates, feature flags, layout wrappers, providers. Clear tree and no ref/displayName issues.
  • HOCs: When integrating with a library that uses HOCs, or when you need to return a “enhanced” component type (e.g. with extra props) and composition doesn’t fit.

Gotchas

  • Refs: HOCs don’t forward refs by default; wrap with forwardRef in the HOC and pass the ref to the inner component.
  • Static methods: HOCs don’t copy static methods from the wrapped component; use a lib like hoist-non-react-statics if you need them.

Compound Components → · Render Props vs Hooks → · All patterns