Composition Patterns That Replace Giant React Components

Break large React components into small pieces with children, explicit slots, and focused components instead of piling props into one file.

7 min read

React composition patterns replace a giant component with small pieces that each own one job. When one file starts owning layout, data, state, and every edge case, splitting it with children, slots, and focused components keeps the code readable.

Spot the giant component

A component is too big when it needs a long prop list to describe every variation, or when it renders several regions that have nothing to do with each other. Imagine a settings page that renders a profile form, a notification list, and a billing summary, each with its own state and network calls. One component holding all three is the giant component this article breaks apart.

Pass children for one flexible region

A wrapper that accepts children stops caring about what goes inside it. A card renders its frame and lets the caller supply the content, so the card stays reusable and the profile stays readable.

App.jsxApp.jsx
function Card({ children }) {
  return <section className="card">{children}</section>;
}
 
function Profile({ user }) {
  return (
    <Card>
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
    </Card>
  );
}

The Card knows nothing about users, and the Profile knows nothing about card styling. Each one changes without touching the other.

The same pattern scales to modals, panels, and list items: the wrapper owns the frame and the caller owns the content. The full rules for this are in The children Prop in React.

Use slots for several named regions

When a component has a few distinct regions, children is not enough. Named props act as explicit slots, so a layout can accept a sidebar and a main area without knowing what they contain.

App.jsxApp.jsx
function DashboardLayout({ sidebar, main }) {
  return (
    <div className="layout">
      <aside>{sidebar}</aside>
      <main>{main}</main>
    </div>
  );
}
 
function Dashboard({ stats, activity }) {
  return (
    <DashboardLayout
      sidebar={<Sidebar stats={stats} />}
      main={<ActivityFeed items={activity} />}
    />
  );
}

The layout owns only its arrangement, so a settings page and a dashboard can share the same shell with different slots. The Dashboard builds the two regions and passes them in as JSX, which is the same idea covered in How to Pass JSX as a Prop in React.

Split by concern, not by line count

A small component can still be doing too much. Split along concerns: one component fetches, one filters, one renders the list, one renders a single row. Each piece keeps its own state and receives only what it needs as props.

This is the opposite of lifting every value to the top. When state stays local to the piece that uses it, changing one piece does not ripple through the others. The parent only coordinates the pieces it composes.

A product page becomes ProductPage, ProductList, ProductRow, and ProductFilters. The page fetches and composes, the list maps rows, the row renders one item, and the filters own the search input. Each piece is small enough to read without scrolling.

When several components belong to the same interaction, such as a menu with its items, a compound component gives them a shared context without making the caller thread props through every level. A menu, tabs, or select control is a natural fit because every piece shares one open state.

The pattern is a form of composition, not a replacement for it. See Compound Components in React for a working example.

When not to split

Use this checklist before extracting components.

  • Split when one component renders several unrelated regions.
  • Split when a piece is reused in more than one place.
  • Keep a component whole when it does one job and reads cleanly top to bottom.
  • Avoid adding a prop for every variation; compose the variation as children or a slot instead.

The goal is fewer responsibilities per file, not a maximum number of files. If a split forces you to pass the same five props through three layers, the component was not the problem. When too many props pile up, the fix is usually a better composition shape, not a deeper split.

Rune AI

Rune AI

Key Insights

  • Split a component that owns several unrelated jobs.
  • Pass children to let a wrapper render a single flexible region.
  • Use named props as slots for a header, sidebar, and body.
  • Keep each small component focused and its state local.
  • Do not split a component that is already clear.
RunePowered by Rune AI

Frequently Asked Questions

Is composition always better than a big component?

Not always. Splitting adds indirection, so split when a component has several unrelated jobs or pieces you want to reuse. A small component that does one job is fine as it is.

What is the difference between children and a regular prop?

Both work for composition. The children prop is implicit and reads naturally for a single wrapped region, while named props give you explicit slots such as a header and a body.

Conclusion

Composition replaces a giant component with small pieces that each own one job. Pass children for a single wrapped region, use named slots for several regions, and reserve compound components for tightly related controls.