Skip to main content

Components and Props

Children and Composition

Written by Published

Anything between a component's opening and closing tags arrives as props.children. It is the difference between a component that configures content and one that wraps it.

The children prop

children is an ordinary prop with a special source: JSX fills it from whatever sits between the tags. That is what lets a component act as a container without knowing what it contains.

A wrapper that knows nothing

jsx

function Panel({ title, children }) {
  return (
    <section style={{ border: "1px solid #cbd5e1", borderRadius: 10, padding: 14 }}>
      <h4 style={{ margin: "0 0 8px" }}>{title}</h4>
      {children}
    </section>
  )
}

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <div style={{ display: "grid", gap: 12 }}>
    <Panel title="Text inside">
      <p>Just a paragraph.</p>
    </Panel>

    <Panel title="Anything else inside">
      <ul>
        <li>A list</li>
        <li>works too</li>
      </ul>
      <button>And a button</button>
    </Panel>
  </div>
)

Panel handles the frame and the heading. What goes inside is entirely the caller's business, which is why one Panel serves both examples without a single option.

Children or a prop?

Both pass content down, so the choice is about who should decide the markup.

  • Use a prop for a value the component will format itself - a title, a count, a date.
  • Use children for arbitrary content the component only positions.
  • If you find yourself passing JSX as a normal prop, children is usually what you wanted.

More than one slot

A component only has one children, but JSX elements are values - so a second region is just another prop that happens to hold JSX.

Two slots

jsx

function SplitPanel({ left, children }) {
  return (
    <div style={{ display: "flex", gap: 12, border: "1px solid #cbd5e1", borderRadius: 10, padding: 12 }}>
      <div style={{ width: 90, color: "#475569" }}>{left}</div>
      <div style={{ flex: 1 }}>{children}</div>
    </div>
  )
}

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <SplitPanel left={<strong>Label</strong>}>
    <p style={{ margin: 0 }}>The main region arrives as children.</p>
  </SplitPanel>
)

Why this matters later

Composition through children is how React avoids most of the problems other systems solve with inheritance or configuration. A layout does not need options for every possible content - it takes children and gets out of the way.

It is also the usual answer to "how do I avoid passing this prop through four levels". Often you can pass the finished element down instead of the data it needs, and the middle layers stop caring.