React composition and the children prop are essential for building reusable components. Learn to avoid prop drilling and create flexible, declarative UIs.

Last month, I spent about three days refactoring a "Card" component that had become a monster. It started with five props, but after a few feature requests, it grew to fifteen, including showIcon, headerText, footerAction, and isDismissible. It was a nightmare to maintain. That’s when I realized I was fighting the framework instead of leaning into react composition.
If you're building a custom UI library, you’ve likely hit this wall. You start by trying to make components "smart" by passing them every possible configuration. But the secret to professional-grade component design patterns isn't adding more props—it's learning how to leave them out.
When you write a component that accepts too many props, you're essentially building an API that you have to support forever. If a junior developer on your team needs a slightly different layout, they’ll ask you for a new prop. You’ll add it, the component grows, and eventually, the logic becomes impossible to test.
We initially tried to solve this with a renderHeader prop. It worked for a week. Then we needed a renderHeaderLeft and a renderHeaderRight. The component became a dumping ground for layout logic. This is the opposite of declarative ui, which should describe what the UI looks like, not how to configure it through a massive settings object.
The most powerful tool for building reusable react components is the children prop. Instead of forcing your component to know how to render a title or an icon, you let the consumer decide.
Think of a Card as a shell. It provides the background, the shadow, and the padding. It doesn't need to know what goes inside. Here is how you shift from rigid props to a composition-based approach:
JSX// Instead of this: // <Card title="Hello" showIcon={true} /> // Do this: <Card> <Card.Header> <Icon name="star" /> <h1>Hello</h1> </Card.Header> <Card.Body> <p>Content goes here.</p> </Card.Body> </Card>
By using the react children prop, you’ve offloaded the responsibility of the layout to the parent. Now, Card.Header can be whatever you want. If you need an image instead of an icon, you don't touch the Card component at all.
Composition is inherently more flexible. When you use components like Card.Header or Card.Body, you are creating a "Compound Component" pattern. It’s a standard in libraries like Radix UI or Headless UI.
The main benefit here is strict separation of concerns. The Card component handles the layout constraints, while the children handle the content. This prevents the "re-render ripple effect" where changing a prop deep in the tree forces the entire container to update. If you're curious about how those updates impact performance, I've written about the react rendering lifecycle before.
To implement this, export your child components as properties of the main component. It makes the API intuitive for other developers:
JSXconst Card = ({ children }) => <div className="card-wrapper">{children}</div>; Card.Header = ({ children }) => <header>{children}</header>; Card.Body = ({ children }) => <div className="card-body">{children}</div>; export default Card;
This approach makes your library feel like a cohesive unit. It’s readable, it’s predictable, and it follows the principle of least power.
Don't go overboard. Not every component needs to be a composition playground. Sometimes, a simple button with a label prop is perfectly fine. If you find yourself over-engineering a simple input field, take a step back.
I’m still occasionally tempted to add "just one more prop" when I'm in a rush. It’s easy to fall back into that trap. The key is to ask yourself: "Am I configuring this component, or am I building a structure?" If it's a structure, composition is almost always the better path.
Does using the children prop affect performance? Generally, no. It can actually improve performance because you’re avoiding massive prop drilling, which often leads to unnecessary re-renders in the middle-tier components.
When should I avoid composition?
Avoid it when the component is atomic, like a basic Button or Badge. If the component doesn't have internal layout logic that benefits from swapping pieces, a standard prop interface is cleaner.
How do I handle state between composed components? For shared state, use the Context API. Wrap your parent component in a Provider, and let the children consume that context. It’s how the big libraries do it, and it keeps your component tree clean.
Building a UI library is an ongoing experiment. I’m still refining how we handle compound component types in TypeScript, as it can get a bit verbose. But moving toward a composition-first mindset has made our codebase significantly easier to navigate.
Master the React Context API to stop prop drilling. Learn when to use it for global state and how to avoid common performance traps in your React apps.
Read more