HomeAbout Me

Advanced React Patterns: Composition

By Daniel Nguyen
Published in React JS
July 02, 2025
2 min read
Advanced React Patterns: Composition

🧩 Composition and Layout Components in React: A Better Way to Share State Without Prop Drilling

TL;DR: The Composition and Layout Components Pattern helps you avoid prop drilling and makes your components more flexible and reusable.

šŸ¤” What’s the Problem with Prop Drilling?

If you’ve ever built a React app with nested components, you’ve probably experienced prop drilling — the tedious process of passing data and functions down through layers of components that don’t even use them.

Before diving into a better pattern, if you’re not familiar with prop drilling, take a moment to read this blog post. It gives a great overview of why it can be such a pain.

Let’s look at a quick example of prop drilling in action:

function App() {
const [count, setCount] = useState(0)
const increment = () => setCount(c => c + 1)
return <Child count={count} increment={increment} />
}
function Child({ count, increment }: { count: number; increment: () => void }) {
return (
<div>
<strong>
I am a child and I don't actually use count or increment. My child does
though, so I have to accept those as props and forward them along.
</strong>
<GrandChild count={count} onIncrementClick={increment} />
</div>
)
}
function GrandChild({
count,
onIncrementClick,
}: {
count: number
onIncrementClick: () => void
}) {
return (
<div>
<small>I am a grandchild and I just pass things off to a button</small>
<button onClick={onIncrementClick}>{count}</button>
</div>
)
}

Notice how Child is forced to accept count and increment, even though it doesn’t use them? This adds coupling and noise, especially as your component tree grows.

Many developers turn to global state solutions or React Context to avoid this, but what if there’s a simpler way?

šŸŽÆ The Composition Pattern: A Cleaner Alternative

Instead of threading state through every layer, what if we just passed React elements instead of data?

Here’s the same functionality rewritten using the Composition and Layout Components Pattern:

function App() {
const [count, setCount] = useState(0)
const increment = () => setCount(c => c + 1)
return (
<Child
grandChild={
<GrandChild
button={<button onClick={increment}>{count}</button>}
/>
}
/>
)
}
function Child({ grandChild }: { grandChild: React.ReactNode }) {
return (
<div>
<strong>
I am a child and I don't actually use count or increment.
</strong>
{grandChild}
</div>
)
}
function GrandChild({ button }: { button: React.ReactNode }) {
return (
<div>
<small>I am a grandchild and I just pass things off to a button</small>
{button}
</div>
)
}

Now the App component manages state and creates the actual UI elements. Child and GrandChild just arrange and display them. Much cleaner, right?

šŸ’” Why This Works

By passing elements instead of data, you:

  • Avoid prop drilling entirely.
  • Keep components focused on their actual purpose (e.g., layout or presentation).
  • Make components more flexible and reusable.
  • Reduce the number of components/files you need to touch when making a change.

Let’s say you want to reuse the Child component but change the GrandChild layout — you don’t need to change how props are passed or restructure your data. You just plug in a different element.

šŸ“¦ When to Use This Pattern

This composition technique is particularly helpful when:

  • You have deeply nested components with state only needed at the top level.
  • You’re building reusable layout or container components.
  • You want to improve testability and decouple logic from layout.

āš ļø Of course, don’t go overboard. You can end up with overly abstract code if you pass every component as a prop. Use this pattern when it adds clarity, not confusion.

🧱 Layout Components: React’s Version of Scoped Slots

If you’re familiar with Vue’s scoped slots, this pattern will feel familiar. In React, layout components are simply components that accept elements via props and render them in a specific structure.

The real magic comes from the fact that the parent owns the logic and the layout component owns the structure.

šŸ“š Real-World Usage

This pattern is not just theoretical. For example, kentcdodds.com uses this extensively — particularly in the hero sections at the top of each page.

Kent also dives deeper into this idea in his blog post: One React mistake that’s slowing you down.

🧪 Try It Yourself

In your next project, try replacing some of your prop chains with this composition pattern. You’ll notice how much more scalable and maintainable your component tree becomes.

For a hands-on exercise, try refactoring a component where you’re passing multiple props down just for a button. Instead, pass the button itself as a React element. Watch how it simplifies everything.


āœ… Takeaways

  • Avoid prop drilling by passing components (React elements) instead of data.
  • Use layout components to manage structure and leave logic to the parent.
  • This pattern makes components easier to reuse and reduces unnecessary dependencies.

Happy composing! šŸ› ļø



Tags

#ReactPatterns

Share

Previous Article
Advanced React Patterns: Introduction

Table Of Contents

1
šŸ¤” What's the Problem with Prop Drilling?
2
šŸŽÆ The Composition Pattern: A Cleaner Alternative
3
šŸ’” Why This Works
4
šŸ“¦ When to Use This Pattern
5
🧱 Layout Components: React’s Version of Scoped Slots
6
šŸ“š Real-World Usage
7
🧪 Try It Yourself

Related Posts

Advanced React Patterns: Control Props
July 09, 2025
2 min
Ā© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media