Skip to main content

Command Palette

Search for a command to run...

React Children vs Slots: When to Use Each Pattern?

When children are enough, when named slots make more sense, and why they are not really competing patterns.

Updated
6 min readView as Markdown
React Children vs Slots: When to Use Each Pattern?

For a long time, when I needed to make a React component flexible, my first thought always was:

Just pass everything through children.

And in many cases, that's exactly what you should do.

But once a component has several places where custom content can go, children can become a little too generic.

That's where the slot pattern becomes useful.

The confusing part is that React doesn't actually have a built-in Slot concept like some other frameworks do.

So what do people mean when they talk about slots in React?

Let's compare.

children: one place for any content

The most basic composition pattern in React is children.

type CardProps = {
  children: React.ReactNode;
};

function Card({ children }: CardProps) {
  return <div className="card">{children}</div>;
}

Then we can put anything inside:

<Card>
  <h2>Profile</h2>
  <p>Some information about the user</p>
</Card>

React passes everything between <Card> and </Card> through the children prop.

I usually think of children as:

Here is one area of the component. Put whatever you want inside it.

This works really well for things like:

  • cards

  • modals

  • layout wrappers

  • providers

  • containers

But what if the component has several separate customizable areas?

What if I need more than one children?

Imagine a modal:

<Modal>
  ...
</Modal>

It might have:

  • a title

  • main content

  • a footer

  • actions

Technically, we could put everything into children:

<Modal>
  <h2>Delete account?</h2>

  <p>This action cannot be undone.</p>

  <div>
    <Button>Cancel</Button>
    <Button>Delete</Button>
  </div>
</Modal>

This is fine if Modal doesn't need to know anything about that structure.

But sometimes it does.

Maybe the modal needs to render the title in one container, the content in another and the actions inside a sticky footer.

Then a single children prop is not very convenient.

Slots are basically named places for content

One common React version of the slot pattern is simply passing React nodes through different props.

type ModalProps = {
  title: React.ReactNode;
  children: React.ReactNode;
  footer?: React.ReactNode;
};

function Modal({ title, children, footer }: ModalProps) {
  return (
    <div className="modal">
      <header>{title}</header>

      <main>{children}</main>

      {footer && <footer>{footer}</footer>}
    </div>
  );
}

Now usage looks like this:

<Modal
  title="Delete account?"
  footer={
    <>
      <Button>Cancel</Button>
      <Button>Delete</Button>
    </>
  }
>
  <p>This action cannot be undone.</p>
</Modal>

Here:

  • children is the main content

  • title is one slot

  • footer is another slot

So, at least conceptually:

children is an unnamed/default slot, while slots give different pieces of content explicit destinations.

There is no special React syntax involved.

They're just props containing React nodes.

Why not use props for everything?

We could go even further:

<Modal
  title="Delete account?"
  description="This action cannot be undone."
  cancelText="Cancel"
  confirmText="Delete"
/>

And sometimes that's better.

The difference is that ordinary data props describe what the component should render, while slots usually let the consumer provide the actual UI.

For example:

footer={<CustomActions />}

gives much more freedom than:

confirmText="Delete"

Neither approach is always better.

It depends on how much control the component should expose.

Slots can make the API clearer

Let's imagine a component with several visual areas.

With generic children, I might need to invent additional wrappers:

<Card>
  <CardHeader>
    <h2>Settings</h2>
  </CardHeader>

  <CardContent>
    ...
  </CardContent>

  <CardFooter>
    <Button>Save</Button>
  </CardFooter>
</Card>

This is the compound component pattern, which is another common way to implement slot-like APIs.

Alternatively, I could make the slots explicit:

<Card
  header={<h2>Settings</h2>}
  footer={<Button>Save</Button>}
>
  ...
</Card>

Both APIs represent roughly the same idea:

This component has several predefined places where custom content can be inserted.

Which one I choose usually depends on the complexity of the component.

For something small, named props are often simpler.

For a larger component with many related parts, compound components can feel more natural.

A slot doesn't have to contain JSX

Another thing that confused me at first was thinking that every customizable area should be a ReactNode.

Sometimes a render function is more useful.

For example:

<List
  items={users}
  renderItem={(user) => (
    <UserCard user={user} />
  )}
/>

renderItem is not technically the same API as:

item={<UserCard />}

because the component passes data back to us.

But conceptually, it's solving a similar composition problem:

The library controls where something is rendered, while I control what gets rendered there.

You'll see this pattern a lot in component libraries.

So when do I use children?

I usually prefer children when the component has one obvious content area.

For example:

<Popover>
  <SettingsForm />
</Popover>

or:

<PageLayout>
  <Dashboard />
</PageLayout>

It's simple, familiar and doesn't add unnecessary API.

When do slots make more sense?

Slots become more useful when a component has several independent customizable areas.

For example:

<Page
  header={<Header />}
  sidebar={<Navigation />}
  footer={<Footer />}
>
  <Content />
</Page>

In this case, trying to put everything into a single children prop would probably make the API less explicit.

Slots also make sense when the component needs to control where each piece is rendered.

children vs slots

children Slots
One default content area Multiple named content areas
Built into React A composition pattern
Passed through props.children Usually implemented with props or compound components
Great for wrappers and containers Great for structured reusable components
Parent mostly provides content Component controls where each piece goes

And the important part:

They're not really competing concepts.

children can actually be one of the slots.

For example:

function Modal({
  title,
  children,
  footer,
}: ModalProps) {
  return (
    <>
      <header>{title}</header>
      <main>{children}</main>
      <footer>{footer}</footer>
    </>
  );
}

Here children is simply the default slot.

Final takeaway

React gives us children, but "slots" are mostly a pattern we build on top of React's normal composition model.

If my component has one customizable area, I usually reach for children.

If it has several distinct areas, I start thinking about named slots:

header
sidebar
footer
actions

or compound components:

<Card.Header />
<Card.Content />
<Card.Footer />

The goal isn't to use the more sophisticated pattern.

It's to make the component API obvious.

If I have to open the component implementation just to understand where my children will end up, that's usually a sign that the API could be clearer.

3 views