Composition over Inheritance

Reuse code by composing components, not by extending them.

React deliberately favors composition over inheritance. You almost never need to extend a component class; instead you build behavior by combining components and passing props and children.

Ways to reuse

  • Pass content via children or named props (slots).
  • Share logic with custom hooks.
  • Wrap components to add behavior, rather than subclassing.

This keeps relationships explicit and components flexible.

Example

Example Β· javascript
// Compose behavior instead of extending
function Dialog({ title, children, footer }) {
  return (
    <div className="dialog">
      <h2>{title}</h2>
      <div>{children}</div>
      <div className="footer">{footer}</div>
    </div>
  );
}

function ConfirmDialog() {
  return (
    <Dialog title="Confirm" footer={<button>OK</button>}>
      <p>Are you sure?</p>
    </Dialog>
  );
}

When to use it

  • A button variant is built by wrapping the base Button with extra props rather than extending a class, so the variant gets all base behaviour for free via composition.
  • A layout library provides a Box component that accepts children so every page section reuses the same padding and breakpoint logic without a shared base class.
  • An analytics higher-order component wraps any component to inject click tracking without modifying the original component's source.

More examples

Specialise via composition not inheritance

Creates a specialised DangerButton by composing Button with a variant prop rather than extending a class.

Example Β· jsx
function Button({ variant = 'default', children, ...rest }) {
  return (
    <button className={`btn btn--${variant}`} {...rest}>
      {children}
    </button>
  );
}

// DangerButton is Button with a variant, not a class extension
function DangerButton(props) {
  return <Button variant="danger" {...props} />;
}

Generic container with children

Provides a generic Panel shell via children so any content can be wrapped without subclassing.

Example Β· jsx
function Panel({ title, children, className = '' }) {
  return (
    <section className={`panel ${className}`.trim()}>
      <header className="panel__header"><h2>{title}</h2></header>
      <div className="panel__body">{children}</div>
    </section>
  );
}

// Reuse for stats, alerts, forms β€” anything
// <Panel title="Revenue"><RevenueChart /></Panel>

Named slot props

Uses named prop slots for each region so callers can inject any component into each part of the layout.

Example Β· jsx
function AppLayout({ sidebar, main, footer }) {
  return (
    <div className="app-layout">
      <aside className="app-layout__sidebar">{sidebar}</aside>
      <main className="app-layout__main">{main}</main>
      <footer className="app-layout__footer">{footer}</footer>
    </div>
  );
}

// Usage
// <AppLayout
//   sidebar={<NavMenu />}
//   main={<DashboardContent />}
//   footer={<FooterLinks />}
// />

Discussion

  • Be the first to comment on this lesson.