Skip to main content

Custom Hooks and Patterns

Component Patterns

Written by Published

A component that takes fourteen props is not configurable. It is several components that have not been separated yet.

Composition over configuration

The instinct when a component needs a variation is to add a prop. Do that a few times and you have a component with a dozen booleans and a body full of conditionals.

The alternative is to accept content rather than options - let the caller pass what goes inside, and keep the component responsible only for structure.

Options versus slots

jsx

// Configuration: every variation needs a new prop.
function ConfiguredCard({ title, showBadge, badgeText, showFooter, footerText }) {
  return (
    <div style={{ border: "1px solid #cbd5e1", padding: 10, borderRadius: 8 }}>
      <b>{title}</b> {showBadge && <span>[{badgeText}]</span>}
      {showFooter && <p style={{ color: "#64748b" }}>{footerText}</p>}
    </div>
  )
}

// Composition: one prop, unlimited variations.
function Card({ children }) {
  return (
    <div style={{ border: "1px solid #cbd5e1", padding: 10, borderRadius: 8 }}>
      {children}
    </div>
  )
}

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <div style={{ display: "grid", gap: 10 }}>
    <ConfiguredCard title="Configured" showBadge badgeText="new" showFooter footerText="a footer" />
    <Card>
      <b>Composed</b> <span>[new]</span>
      <p style={{ color: "#64748b" }}>Anything at all goes here.</p>
    </Card>
  </div>
)

Separating logic from markup

A long-standing pattern splits a component in two: one that fetches and computes, one that only renders what it is given. The second takes props and has no state, which makes it trivial to reuse and to test.

Custom hooks made the split cleaner - the logic goes in a hook rather than a wrapper component, and the presentational component stays exactly as it was.

Signals a component is doing too much

  1. More than about five or six props, especially several booleans.
  2. Props named for what they turn on rather than what they are.
  3. Whole branches of JSX behind an if, rendering unrelated things.
  4. A name with "And" in it, or a vague one like Manager.

Two of these together usually means splitting by use case rather than adding another prop - two components with clear names beat one with a mode switch.

Where to keep things

  • Group by feature, not by type. A folder per feature beats a folder holding every component in the app.
  • Shared, generic components live in one common place; feature-specific ones live with their feature.
  • Co-locate a component's hook, styles and test with it.
  • Move something to shared when a second feature needs it, not in anticipation.