Parents & Owners in React: Data Flow

Published Last updated v1.0.1

React composes. React components render other components. This way, we assemble components into a hierarchy — a tree — that forms the UI.

Let’s do a quick thought experiment: What if you gave the same UI design to 100 React devs to turn it into an app? It would have to look and behave the same.

Would everyone break down the same visual elements in to components? How many components would they need? How granular or monolithic would their components be? Would there be ‘natural fault lines’ that everyone follows? And how are those components put together?

It would make an interesting experiment. No doubt. There are many ways to compose components and get the same UI. I’m sure the code would be different for all 100. Everyone has their own habits, preferences, and experience level. There is no one right way to do it.

That’s why deciding how to compose components is one of hardest things in React! But some of those apps would be better structured than others. What makes a React app well-composed? I’d say:

  • An easy-to-follow data flow
  • Well-encapsulated components
  • Good rendering performance

Consider this small app.

Each component is in control of what content it returns. This works fine.


_68
function App() {
_68
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_68
_68
return (
_68
<div>
_68
<Header />
_68
<div>
_68
{currentUser ? (
_68
<Dashboard user={currentUser} />
_68
) : (
_68
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_68
)}
_68
</div>
_68
<Footer />
_68
</div>
_68
);
_68
}
_68
_68
function Header() {
_68
return (
_68
<header style={{ backgroundColor: "lightgray" }}>
_68
<h2>Header</h2>
_68
</header>
_68
);
_68
}
_68
_68
function Footer() {
_68
return (
_68
<footer style={{ backgroundColor: "lightgray" }}>
_68
<h2>Footer</h2>
_68
</footer>
_68
);
_68
}
_68
_68
function Dashboard({ user }) {
_68
return (
_68
<main>
_68
<h2>The Dashboard</h2>
_68
<DashboardNav />
_68
<DashboardContent user={user} />
_68
</main>
_68
);
_68
}
_68
_68
function DashboardNav() {
_68
return (
_68
<nav>
_68
<h3>Dashboard Nav</h3>
_68
</nav>
_68
);
_68
}
_68
_68
function DashboardContent({ user }) {
_68
return (
_68
<div>
_68
<h3>Dashboard Content</h3>
_68
<WelcomeMessage user={user} />
_68
</div>
_68
);
_68
}
_68
_68
function WelcomeMessage({ user }) {
_68
return (
_68
<div>
_68
<p>Welcome {user.name}</p>
_68
</div>
_68
);
_68
}

First, we’ll ignore a few less relevant components.


_44
function App() {
_44
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_44
_44
return (
_44
<div>
_44
<Header />
_44
<div>
_44
{currentUser ? (
_44
<Dashboard user={currentUser} />
_44
) : (
_44
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_44
)}
_44
</div>
_44
<Footer />
_44
</div>
_44
);
_44
}
_44
_44
function Dashboard({ user }) {
_44
return (
_44
<main>
_44
<h2>The Dashboard</h2>
_44
<DashboardNav />
_44
<DashboardContent user={user} />
_44
</main>
_44
);
_44
}
_44
_44
function DashboardContent({ user }) {
_44
return (
_44
<div>
_44
<h3>Dashboard Content</h3>
_44
<WelcomeMessage user={user} />
_44
</div>
_44
);
_44
}
_44
_44
function WelcomeMessage({ user }) {
_44
return (
_44
<div>
_44
<p>Welcome {user.name}</p>
_44
</div>
_44
);
_44
}

The currentUser state has to go from <App> at the top to <WelcomeMessage> at the bottom.


_44
function App() {
_44
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_44
_44
return (
_44
<div>
_44
<Header />
_44
<div>
_44
{currentUser ? (
_44
<Dashboard user={currentUser} />
_44
) : (
_44
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_44
)}
_44
</div>
_44
<Footer />
_44
</div>
_44
);
_44
}
_44
_44
function Dashboard({ user }) {
_44
return (
_44
<main>
_44
<h2>The Dashboard</h2>
_44
<DashboardNav />
_44
<DashboardContent user={user} />
_44
</main>
_44
);
_44
}
_44
_44
function DashboardContent({ user }) {
_44
return (
_44
<div>
_44
<h3>Dashboard Content</h3>
_44
<WelcomeMessage user={user} />
_44
</div>
_44
);
_44
}
_44
_44
function WelcomeMessage({ user }) {
_44
return (
_44
<div>
_44
<p>Welcome {user.name}</p>
_44
</div>
_44
);
_44
}

Therefore each intermediate component (<Dashboard> & <DashboardContent>) has to pass user forward, while having no use for it itself. That makes user a false dependency for these components.


_44
function App() {
_44
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_44
_44
return (
_44
<div>
_44
<Header />
_44
<div>
_44
{currentUser ? (
_44
<Dashboard user={currentUser} />
_44
) : (
_44
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_44
)}
_44
</div>
_44
<Footer />
_44
</div>
_44
);
_44
}
_44
_44
function Dashboard({ user }) {
_44
return (
_44
<main>
_44
<h2>The Dashboard</h2>
_44
<DashboardNav />
_44
<DashboardContent user={user} />
_44
</main>
_44
);
_44
}
_44
_44
function DashboardContent({ user }) {
_44
return (
_44
<div>
_44
<h3>Dashboard Content</h3>
_44
<WelcomeMessage user={user} />
_44
</div>
_44
);
_44
}
_44
_44
function WelcomeMessage({ user }) {
_44
return (
_44
<div>
_44
<p>Welcome {user.name}</p>
_44
</div>
_44
);
_44
}

This is known as prop drilling.

Prop drilling can make your app hard to maintain. Over time, as product requirements change, so will your app. It should be prepared for that.

For example, the designer wants to turn <WelcomeMessage> in to a floating element in <App/>. Now you have to remove the user prop from all of its parent components.


_45
function App() {
_45
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_45
_45
return (
_45
<div>
_45
<Header />
_45
<div>
_45
{currentUser ? (
_45
<Dashboard />
_45
) : (
_45
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_45
)}
_45
</div>
_45
<WelcomeMessage user={currentUser} />
_45
<Footer />
_45
</div>
_45
);
_45
}
_45
_45
function Dashboard() {
_45
return (
_45
<main>
_45
<h2>The Dashboard</h2>
_45
<DashboardNav />
_45
<DashboardContent />
_45
</main>
_45
);
_45
}
_45
_45
function DashboardContent() {
_45
return (
_45
<div>
_45
<h3>Dashboard Content</h3>
_45
<div>Some content</div>
_45
</div>
_45
);
_45
}
_45
_45
function WelcomeMessage({ user }) {
_45
return (
_45
<div>
_45
<p>Welcome {user.name}</p>
_45
</div>
_45
);
_45
}

Whenever <WelcomeMessage> needs more props from the top, you have to add them at all the intermediate levels too.

These sorts of things make change tedious and can become time-consuming in large applications.


_45
function App() {
_45
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_45
const [personalMessage, setPersonalMessage] = useState();
_45
_45
return (
_45
<div>
_45
<Header />
_45
<div>
_45
{currentUser ? (
_45
<Dashboard user={currentUser} personalMessage={personalMessage} />
_45
) : (
_45
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_45
)}
_45
</div>
_45
<Footer />
_45
</div>
_45
);
_45
}
_45
_45
function Dashboard({ user, personalMessage }) {
_45
return (
_45
<main>
_45
<h2>The Dashboard</h2>
_45
<DashboardNav />
_45
<DashboardContent user={user} personalMessage={personalMessage} />
_45
</main>
_45
);
_45
}
_45
_45
function DashboardContent({ user, personalMessage }) {
_45
return (
_45
<div>
_45
<h3>Dashboard Content</h3>
_45
<WelcomeMessage user={user} personalMessage={personalMessage} />
_45
</div>
_45
);
_45
}
_45
_45
function WelcomeMessage({ user, personalMessage }) {
_45
return (
_45
<div>
_45
<p>Welcome {user.name} {personalMessage}</p>
_45
</div>
_45
);
_45
}

Consider this small app.

Each component is in control of what content it returns. This works fine.

First, we’ll ignore a few less relevant components.

The currentUser state has to go from <App> at the top to <WelcomeMessage> at the bottom.

Therefore each intermediate component (<Dashboard> & <DashboardContent>) has to pass user forward, while having no use for it itself. That makes user a false dependency for these components.

This is known as prop drilling.

Prop drilling can make your app hard to maintain. Over time, as product requirements change, so will your app. It should be prepared for that.

For example, the designer wants to turn <WelcomeMessage> in to a floating element in <App/>. Now you have to remove the user prop from all of its parent components.

Whenever <WelcomeMessage> needs more props from the top, you have to add them at all the intermediate levels too.

These sorts of things make change tedious and can become time-consuming in large applications.


_68
function App() {
_68
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_68
_68
return (
_68
<div>
_68
<Header />
_68
<div>
_68
{currentUser ? (
_68
<Dashboard user={currentUser} />
_68
) : (
_68
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_68
)}
_68
</div>
_68
<Footer />
_68
</div>
_68
);
_68
}
_68
_68
function Header() {
_68
return (
_68
<header style={{ backgroundColor: "lightgray" }}>
_68
<h2>Header</h2>
_68
</header>
_68
);
_68
}
_68
_68
function Footer() {
_68
return (
_68
<footer style={{ backgroundColor: "lightgray" }}>
_68
<h2>Footer</h2>
_68
</footer>
_68
);
_68
}
_68
_68
function Dashboard({ user }) {
_68
return (
_68
<main>
_68
<h2>The Dashboard</h2>
_68
<DashboardNav />
_68
<DashboardContent user={user} />
_68
</main>
_68
);
_68
}
_68
_68
function DashboardNav() {
_68
return (
_68
<nav>
_68
<h3>Dashboard Nav</h3>
_68
</nav>
_68
);
_68
}
_68
_68
function DashboardContent({ user }) {
_68
return (
_68
<div>
_68
<h3>Dashboard Content</h3>
_68
<WelcomeMessage user={user} />
_68
</div>
_68
);
_68
}
_68
_68
function WelcomeMessage({ user }) {
_68
return (
_68
<div>
_68
<p>Welcome {user.name}</p>
_68
</div>
_68
);
_68
}

Let us deviate for a second to go over how content of components can be defined. Components can be fully in control of their own content, like in the example above — or components can leave it up to a parent to place content inside of them.

A component can wrap content by nesting it between its tags, just like HTML. The wrapped content is passed down as the special children prop 1.

You can think of a component with a children prop as having a ‘slot’ that can be filled in by its parent components 2. I like to think of children as the default slot provided by React. If you need multiple slots, you can pass JSX as props too 3.

Strangely enough, there is no widely-recognized name for such components. I’ve heard ‘layout’, ‘wrapper’, ‘container’, and ‘slotted’ components. I’ll go with slotted components in this post.

Let’s turn <Dashboard> into a slotted component:


_16
function App() {
_16
return (
_16
<Dashboard>
_16
<DashboardContent currentUser={currentUser} />
_16
</Dashboard>
_16
);
_16
}
_16
_16
function Dashboard({ children }) {
_16
return (
_16
<div>
_16
<h2>Dashboard</h2>
_16
{children} {/* DashboardContent will be rendered here */}
_16
</div>
_16
);
_16
}

Recognize that there are two different relations between components:

  1. Parent: the component or in which the other component is nested.
  2. Owner: the component which renders the other component 4.

I think this distinction is overlooked. It’s important because props come from owners. Sometimes an component’s parent is also its owner, but usually they're different. A parent can only pass props when it is an owner as well.

In this example:

  • <App> is the parent and owner of <Dashboard/>
  • <Dashboard> is the parent of <DashboardNav> and <DashboardContent> but not the owner. <App> places <DashboardContent> inside <Dashboard> so it is the owner.
Source-to-surface measure for props

A useful measure for prop drilling is the source-to-surface: the number of components some piece of data needs to pass through to surface in the UI.

To find it, answer the following: Where is a piece of data defined and where does it surface in the UI?

  • Where is it defined?
    • Which component holds the state
    • Where is the 'static prop' passed (e.g. to configure a component)
  • Where is it used?
    • Where is it visible? Where is it used in render?
    • It can be ‘invisible’: In what event handler/effect is it used?

Then count the number of component it has to pass through. A lower source-to-surface (meaning a shorter path through component tree) is generally better.

How are slotted components useful? Let’s look at our app again.


_44
function App() {
_44
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_44
_44
return (
_44
<div>
_44
<Header />
_44
<div>
_44
{currentUser ? (
_44
<Dashboard user={currentUser} />
_44
) : (
_44
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_44
)}
_44
</div>
_44
<Footer />
_44
</div>
_44
);
_44
}
_44
_44
function Dashboard({ user }) {
_44
return (
_44
<main>
_44
<h2>The Dashboard</h2>
_44
<DashboardNav />
_44
<DashboardContent user={user} />
_44
</main>
_44
);
_44
}
_44
_44
function DashboardContent({ user }) {
_44
return (
_44
<div>
_44
<h3>Dashboard Content</h3>
_44
<WelcomeMessage user={user} />
_44
</div>
_44
);
_44
}
_44
_44
function WelcomeMessage({ user }) {
_44
return (
_44
<div>
_44
<p>Welcome {user.name}</p>
_44
</div>
_44
);
_44
}

If we turn <Dashboard> and <DashboardContent> into slotted components…


_48
function App() {
_48
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_48
_48
return (
_48
<div>
_48
<Header />
_48
<div>
_48
{currentUser ? (
_48
<Dashboard>
_48
<DashboardNav />
_48
<DashboardContent>
_48
<WelcomeMessage user={currentUser} />
_48
</DashboardContent>
_48
</Dashboard>
_48
) : (
_48
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_48
)}
_48
</div>
_48
<Footer />
_48
</div>
_48
);
_48
}
_48
_48
function Dashboard({ children }) {
_48
return (
_48
<main>
_48
<h2>The Dashboard</h2>
_48
{children}
_48
</main>
_48
);
_48
}
_48
_48
function DashboardContent({ children }) {
_48
return (
_48
<div>
_48
<h3>Dashboard Content</h3>
_48
{children}
_48
</div>
_48
);
_48
}
_48
_48
function WelcomeMessage({ user }) {
_48
return (
_48
<div>
_48
<p>Welcome {user.name}</p>
_48
</div>
_48
);
_48
}

  • <App> is still the parent and owner of <Dashboard>
  • Now <Dashboard> is the parent of <DashboardContent> but not the owner
  • Likewise, <DashboardContent> is the parent of <WelcomeMessage> but not the owner
  • <App> now owns <DashboardContent> and <WelcomeMessage>

Because <WelcomeMessage> is lifted to <App>, currentUser can be passed to it directly. The source-to-surface has gone from 3 to 1.


_48
function App() {
_48
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_48
_48
return (
_48
<div>
_48
<Header />
_48
<div>
_48
{currentUser ? (
_48
<Dashboard>
_48
<DashboardNav />
_48
<DashboardContent>
_48
<WelcomeMessage user={currentUser} />
_48
</DashboardContent>
_48
</Dashboard>
_48
) : (
_48
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_48
)}
_48
</div>
_48
<Footer />
_48
</div>
_48
);
_48
}
_48
_48
function Dashboard({ children }) {
_48
return (
_48
<main>
_48
<h2>The Dashboard</h2>
_48
{children}
_48
</main>
_48
);
_48
}
_48
_48
function DashboardContent({ children }) {
_48
return (
_48
<div>
_48
<h3>Dashboard Content</h3>
_48
{children}
_48
</div>
_48
);
_48
}
_48
_48
function WelcomeMessage({ user }) {
_48
return (
_48
<div>
_48
<p>Welcome {user.name}</p>
_48
</div>
_48
);
_48
}

Additionally, <Dashboard> and <DashboardContent> are no longer passed irrelevant data. They’re disentangled from their content and therefore better encapsulated. This makes our — admittedly very small — data flow is easier to follow! And we didn’t even have to reach for React Context.


_50
_50
function App() {
_50
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_50
_50
return (
_50
<div>
_50
<Header />
_50
<div>
_50
{currentUser ? (
_50
<Dashboard>
_50
<DashboardNav />
_50
<DashboardContent>
_50
<WelcomeMessage user={currentUser} />
_50
</DashboardContent>
_50
</Dashboard>
_50
) : (
_50
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_50
)}
_50
</div>
_50
<Footer />
_50
</div>
_50
);
_50
_50
}
_50
_50
function Dashboard({ children }) {
_50
return (
_50
<main>
_50
<h2>The Dashboard</h2>
_50
{children}
_50
</main>
_50
);
_50
}
_50
_50
function DashboardContent({ children }) {
_50
return (
_50
<div>
_50
<h3>Dashboard Content</h3>
_50
{children}
_50
</div>
_50
);
_50
}
_50
_50
function WelcomeMessage({ user }) {
_50
return (
_50
<div>
_50
<p>Welcome {user.name}</p>
_50
</div>
_50
);
_50
}

How are slotted components useful? Let’s look at our app again.

If we turn <Dashboard> and <DashboardContent> into slotted components…

  • <App> is still the parent and owner of <Dashboard>
  • Now <Dashboard> is the parent of <DashboardContent> but not the owner
  • Likewise, <DashboardContent> is the parent of <WelcomeMessage> but not the owner
  • <App> now owns <DashboardContent> and <WelcomeMessage>

Because <WelcomeMessage> is lifted to <App>, currentUser can be passed to it directly. The source-to-surface has gone from 3 to 1.

Additionally, <Dashboard> and <DashboardContent> are no longer passed irrelevant data. They’re disentangled from their content and therefore better encapsulated. This makes our — admittedly very small — data flow is easier to follow! And we didn’t even have to reach for React Context.


_44
function App() {
_44
const [currentUser, setCurrentUser] = useState({ name: "Jules" });
_44
_44
return (
_44
<div>
_44
<Header />
_44
<div>
_44
{currentUser ? (
_44
<Dashboard user={currentUser} />
_44
) : (
_44
<LoginScreen onLogin={() => setCurrentUser({ name: "Michael" })} />
_44
)}
_44
</div>
_44
<Footer />
_44
</div>
_44
);
_44
}
_44
_44
function Dashboard({ user }) {
_44
return (
_44
<main>
_44
<h2>The Dashboard</h2>
_44
<DashboardNav />
_44
<DashboardContent user={user} />
_44
</main>
_44
);
_44
}
_44
_44
function DashboardContent({ user }) {
_44
return (
_44
<div>
_44
<h3>Dashboard Content</h3>
_44
<WelcomeMessage user={user} />
_44
</div>
_44
);
_44
}
_44
_44
function WelcomeMessage({ user }) {
_44
return (
_44
<div>
_44
<p>Welcome {user.name}</p>
_44
</div>
_44
);
_44
}

We must recognize two — related but different — component hierarchies.

  1. The parent tree: which components are nested inside of another. This is what you see in the React DevTools “⚛️ Components” tab.
  2. The owner tree: which component renders another components. This is the hierarchy we see in code.

The parent tree is what is created by rendering. The owner tree is how it is created. Having a clear idea of this distinction is key to composing well.

Everything is clearer as a diagram, so here’s one for the parent tree. The dashed arcs between components indicate ownership. The moving dot represents the user prop trickling down.

Ill-composed Parent TreeApp (3)Appdiv (4)App/divHeader (5)App/div/Headerdiv (8)App/div/divFooter (23)App/div/Footerheader (6)App/div/Header/headerDashboard (9)App/div/div/Dashboardfooter (24)App/div/Footer/footerh2 (7)App/div/Header/header/h2main (10)App/div/div/Dashboard/mainh2 (25)App/div/Footer/footer/h2h2 (11)App/div/div/Dashboard/main/h2DashboardNav (12)App/div/div/Dashboard/main/DashboardNavDashboardContent (15)App/div/div/Dashboard/main/DashboardContentnav (13)App/div/div/Dashboard/main/DashboardNav/navdiv (16)App/div/div/Dashboard/main/DashboardContent/divh3 (14)App/div/div/Dashboard/main/DashboardNav/nav/h3h3 (17)App/div/div/Dashboard/main/DashboardContent/div/h3WelcomeMessage (18)App/div/div/Dashboard/main/DashboardContent/div/WelcomeMessagediv (19)"App/div/div/Dashboard/main/DashboardContent/div/WelcomeMessage/div"p (20)App/div/div/Dashboard/main/DashboardContent/div/WelcomeMessage/div/p

Take a moment to relate the diagram to the code then hit the toggle above and compare.

Notice that the parent tree is exactly* the same as before! And by extension, the resulting DOM tree and UI too.

* What's the [] in the diagram of the updated version?

Structurally, the tree is the same. When you wrap multiple children in a slotted component using children, React passes the children in array to the parent.

If you would pass this JSX as a named prop, you would have to wrap it in a <Fragment>. With children, React (kind of) does this for you. The [] is like an implicit <Fragment>. Consider it an implementation artifact.

Here’s the owner tree:

Ill-composed Owner TreeApp (3)div (4)Header (5)div (8)Footer (23)Dashboard (9)header (6)h2 (7)footer (24)h2 (25)main (10)h2 (11)DashboardNav (12)DashboardContent (15)nav (13)h3 (14)div (16)h3 (17)WelcomeMessage (18)div (19)p (20)

Notice how it has changed. It has become flatter by using slotted components to lift up <DashboardContent> and <WelcomeMessage>.

Different owner trees can generate identical parent trees. Data flow — props — follows the owner tree! Flattening the owner hierarchy by lifting components can make the data flow shorter, resulting in better encapsulated components.

Lifting components can help rendering performance too. Props follows the owner tree, so updates do so too. We can use this to seperate components such that updates are avoided in parts that don’t need to be updated.

This is really a subject of its own so I won’t go into it here. I will (probably) do a follow-up post on this, including diagrams of course.

I’ve been scribbling these trees using pen and paper whenever my components were becoming a tangle. While writing this post, I learned that you can view the owner hierarchy in the React DevTools by double clicking on a component in the component tree (see tutorial). Who knew!

Screenshot of React Devtools showing the owner tree for the ill-composed example appScreenshot of React Devtools showing the owner tree for the well-composed example app

The React DevTools showing the owner tree for both versions of the app.

React is fundamentally about composition. Composition has always been relevant for maintainable, performant components. It will become even more relevant with React Server Components, where children can be received from the server. I must confess I haven’t looked at RSC that much so I’ll leave it for another time.

  • Understanding the owner vs parent component distinction is important for composing well.
    • I feel this distinction is under-discussed.
    • They create similar but different hierarchies. They’re related, which makes it easy to mix them up.
    • The owner tree is the shape of the data flow.
      • If the data flow is a mess, look at the owner trees. This can reveal when it’s a good idea to lift a component up.
  • Slotted component can skip a level in the owner tree while creating the same parent tree.
    • Which is useful when ‘prop drilling’.

To close on a nuanced note: passing props down a few levels is not necessarily bad. And giving more control to parents by lifting components isn’t always the right solution.

This inversion of control [lifting components] can make your code cleaner in many cases by reducing the amount of props you need to pass through your application and giving more control to the root components.

Such inversion, however, isn’t the right choice in every case; moving more complexity higher in the tree makes those higher-level components more complicated and forces the lower-level components to be more flexible than you may want.

There’s a right time and place for every pattern, recognizing when and where is what it’s all about. I hope this post and these diagrams helped with that.

The example comes from Micheal Jackson's video Using Composition in React to Avoid "Prop Drilling". Credits to him for coming up with it and explaining it well.

Credits to Marcos for proofreading and providing valuable feedback.

  1. props.children is available on every component. It contains the content between the opening and closing tags of a component. Legacy React Docs: Glossary of React Terms – props.children

  2. When you nest content inside a JSX tag, the parent component will receive that content in a prop called children. […] You can think of a component with a children prop as having a “hole” that can be “filled in” by its parent components with arbitrary JSX React Docs: Passing Props to a Component - Passing JSX as children

  3. Some components don’t know their children ahead of time. This is especially common for components like Sidebar or Dialog that represent generic “boxes”. We recommend that such components use the special children prop to pass children elements directly into their output: [...] While this is less common, sometimes you might need multiple “holes” in a component. In such cases you may come up with your own convention instead of using children: React elements like <Contacts /> and <Chat /> are just objects, so you can pass them as props like any other data. This approach may remind you of “slots” in other libraries but there are no limitations on what you can pass as props in React. Legacy React Docs: Composition vs Inheritance - Containment

  4. In React, an element's owner refers to the thing that rendered it. Sometimes an element's parent is also its owner, but usually they're different. This distinction is important because props come from owners. React DevTools Tutorial