patternjavascriptreactTip
Component composition over prop drilling — slots pattern
Viewed 0 times
compositionprop drillingslotschildren propnamed slotslayout componentcomponent API
Problem
Passing props five levels deep to reach a component that needs them — prop drilling — makes components fragile (every intermediate component must forward the prop) and harder to refactor.
Solution
Lift rendering up and pass components as children or named slots:
// BAD: drilling theme through intermediaries
<App theme={theme}>
<Layout theme={theme}>
<Sidebar theme={theme}>
<NavItem theme={theme} />
</Sidebar>
</Layout>
</App>
// GOOD: composition — components receive their dependencies at the call site
<App>
<Layout
sidebar={
<Sidebar>
<NavItem theme={theme} />{/ theme applied here, not drilled /}
</Sidebar>
}
main={<Main />}
/>
</App>
// Layout.jsx — doesn't know about theme at all
function Layout({ sidebar, main }) {
return (
<div className="layout">
<aside>{sidebar}</aside>
<main>{main}</main>
</div>
);
}
// Named slots pattern (more explicit than children)
function Card({ header, body, footer }) {
return (
<div className="card">
<div className="card-header">{header}</div>
<div className="card-body">{body}</div>
<div className="card-footer">{footer}</div>
</div>
);
}
// BAD: drilling theme through intermediaries
<App theme={theme}>
<Layout theme={theme}>
<Sidebar theme={theme}>
<NavItem theme={theme} />
</Sidebar>
</Layout>
</App>
// GOOD: composition — components receive their dependencies at the call site
<App>
<Layout
sidebar={
<Sidebar>
<NavItem theme={theme} />{/ theme applied here, not drilled /}
</Sidebar>
}
main={<Main />}
/>
</App>
// Layout.jsx — doesn't know about theme at all
function Layout({ sidebar, main }) {
return (
<div className="layout">
<aside>{sidebar}</aside>
<main>{main}</main>
</div>
);
}
// Named slots pattern (more explicit than children)
function Card({ header, body, footer }) {
return (
<div className="card">
<div className="card-header">{header}</div>
<div className="card-body">{body}</div>
<div className="card-footer">{footer}</div>
</div>
);
}
Why
When a component receives its content as props (children or named slots), it becomes a layout container — it doesn't know or care about the data its content needs. The parent that provides the content also has access to the data, eliminating the need to pass it down through intermediaries.
Gotchas
- Context is the right tool when you need the same value across many components in a deep tree — composition solves drilling for structural components
- children is a single slot — use named props for multiple independent slots
- This pattern can make parent components more complex — balance flexibility with readability
- Compound components (Tabs.Tab, Tabs.Panel) are an advanced version of this pattern
Code Snippets
Named slots via props
// Slots pattern
function Dialog({ title, body, actions }) {
return (
<div className="dialog">
<header>{title}</header>
<section>{body}</section>
<footer>{actions}</footer>
</div>
);
}
<Dialog
title={<h2>Confirm Delete</h2>}
body={<p>This cannot be undone.</p>}
actions={<><Button onClick={cancel}>Cancel</Button><Button onClick={confirm}>Delete</Button></>}
/>Context
When props are being passed through multiple intermediary components that don't use them
Revisions (0)
No revisions yet.