Children over render props

Pass structure as children. Keep render props for handing data back.

From vercel-composition-patterns, rule children-over-render-props.

Customize the footer

Apply each change. Compare the code you touch.

renderX propsBad
3 callback slots
last edit ±0 lines0 lines total
renderFooter replaces the default
childrenGood
plain JSX, no signatures
last edit ±0 lines0 lines total
add, move or drop a line
function ComposerFrame({ children }: { children: React.ReactNode }) {
  return <form>{children}</form>
}

function ComposerFooter({ children }: { children: React.ReactNode }) {
  return <footer className='flex'>{children}</footer>
}

// Usage is flexible
return (
  <Composer.Frame>
    <CustomHeader />
    <Composer.Input />
    <Composer.Footer>
      <Composer.Formatting />
      <Composer.Emojis />
      <SubmitButton />
    </Composer.Footer>
  </Composer.Frame>
)

When the parent owns the data

Scroll both lists. Count the rows each one builds.

children for rowsBad
0 rows built0 ms to mount
{items.map((item) => <Row item={item} />)}
renderItemGood
0 rows on screen0 renderItem calls
renderItem={({ item, index }) => …}
// Render props work well when you need to pass data back
<List
  data={items}
  renderItem={({ item, index }) => <Item item={item} index={index} />}
/>

Why it matters, pros and cons

Why it matters

Children read like the UI they build. A render prop earns its place only when the parent hands data back.

Pros

  • No callback signatures to learn.
  • Reorder or insert by moving one line.
  • Defaults stay; you add instead of replace.

Cons

  • A list that picks visible rows needs renderItem.
  • Children get no per-row data without context.
  • A slot with one fixed spot can stay a named prop.