Children provides utilities for working with the opaque props.children data structure.
Why Children Utilities?
props.children can be:
- A single element
- An array of elements
- A string
- A number
- null/undefined
- A fragment
The Children utilities handle all these cases consistently.
Children.map
Transform each child, handling all edge cases:
tsximport { Children } from 'react'; function RowList({ children }) { return ( <div className="rows"> {Children.map(children, (child, index) => ( <div className="row" key={index}> {child} </div> ))} </div> ); }
Children.forEach
Iterate without returning:
tsxfunction validateChildren({ children }) { Children.forEach(children, (child) => { if (child.type !== ListItem) { console.warn('List only accepts ListItem children'); } }); }
Children.count
Count the number of children:
tsxfunction TabList({ children }) { const count = Children.count(children); return ( <div> <span>{count} tabs</span> {children} </div> ); }
Children.only
Assert and return the only child:
tsxfunction OnlyChild({ children }) { // Throws if children is not exactly one element const child = Children.only(children); return <div className="wrapper">{child}</div>; }
Children.toArray
Convert children to a flat array:
tsxfunction Reverse({ children }) { const childArray = Children.toArray(children); return <>{childArray.reverse()}</>; } function Interleave({ children, separator }) { const childArray = Children.toArray(children); return ( <> {childArray.map((child, i) => ( <Fragment key={i}> {i > 0 && separator} {child} </Fragment> ))} </> ); } // Usage <Interleave separator=" | "> <span>A</span> <span>B</span> <span>C</span> </Interleave> // Renders: A | B | C
⚠️ Limitations and Alternatives
Children utilities have limitations:
- Can't see inside fragments - Fragments are treated as single children
- Brittle - Depends on specific children structure
- Hard to type - TypeScript struggles with children manipulation
Prefer These Patterns:
tsx// 🔴 Manipulating children function Tabs({ children }) { return Children.map(children, (child, i) => cloneElement(child, { index: i }) ); } // ✅ Render prop function Tabs({ items, renderTab }) { return items.map((item, i) => renderTab(item, i)); } // ✅ Compound components with context function Tabs({ children }) { const [activeIndex, setActiveIndex] = useState(0); return ( <TabsContext.Provider value={{ activeIndex, setActiveIndex }}> {children} </TabsContext.Provider> ); }