In React, what does a component's `children` prop contain, how does JSX populate it, and what shapes can its value take at runtime?
answer
- nested JSX becomes a prop
- same props object as the others
- shape depends on how many
- one child is not an array
- undefined when nothing nested
basics
~20 schildren is an ordinary React prop that holds whatever JSX you nest between a component's opening and closing tags. Its value may be a string, a single element, an array of nodes, or undefined when nothing is nested.
solid answer
~40 s`children` is not a special language feature — it is a normal prop with a reserved name. JSX takes everything nested between the opening and closing tags of `<Card>…</Card>` and puts it into the props object under the key `children`, alongside `title` or any other attribute. The value's shape depends on what you nested: one text node gives you a string, one element gives you that element object, several nested nodes give you an array, and `<Card />` with nothing inside leaves `children` as `undefined`. Because the shape is unstable, the component should render it (`{children}`) rather than index into it. Accepting `children` is what makes a component a container: `Card`, `Modal` and `Layout` can wrap arbitrary content without knowing anything about it, which is React's replacement for extending a base component.
code
jsx · 19 linesfunction Card({ title, children }) {
return (
<section className="card">
<h2>{title}</h2>
<div>{children}</div>
</section>
);
}
export default function Demo() {
return (
<>
<Card title="String">Just text</Card>
<Card title="Element"><p>One node</p></Card>
<Card title="Array"><p>First</p><p>Second</p></Card>
<Card title="Undefined" />
</>
);
}go deeper
Be able to write a wrapper component that renders {children} and to say plainly that nested JSX arrives as an ordinary prop. Know that a single child is not an array.
Explain how JSX turns nested content into the children key of the props object, list the shapes the value can take, and say why elements are inert descriptions until React renders them.
Show the judgment: argue for a container that accepts arbitrary children over one that grows a configuration prop per use case, and point at the maintenance cost of components that inspect what was nested.
Own the API-design angle — accepting children is a stability commitment, because a container that never inspects its content keeps working for consumers you have not met. Weigh that openness against the constraints a stricter, typed child contract buys you.
## children is an ordinary prop React components receive exactly one argument: a props object. JSX attributes become keys in that object, and JSX has one extra rule — whatever you nest between the opening and closing tags becomes the prop named `children`. So this JSX: ```jsx <Card title="Stats"> <Chart /> </Card> ``` produces a props object shaped roughly like `{ title: "Stats", children: <Chart /> }`. `children` sits in the same object as `title`; nothing about it is magic. You may even pass it as an explicit attribute (`<Card children={<Chart />} />`), though nesting is the convention because it reads like markup. The component decides where that content lands, by writing `{children}` somewhere in its own JSX: ```jsx function Card({ title, children }) { return ( <section className="card"> <h2>{title}</h2> <div className="card-body">{children}</div> </section> ); } ``` If a component never renders `{children}`, nested content simply does not appear — there is no implicit slot. ## The shapes the value can take This is where beginners get burned, because `children` is not consistently an array: - `<Card>Hello</Card>` → the string `"Hello"`. - `<Card>{42}</Card>` → the number `42`. - `<Card><p /></Card>` → a single React element object. - `<Card><p /><p /></Card>` → an array of two elements. - `<Card />` → `undefined`. - `<Card>{items.map(i => <Row key={i.id} />)}</Card>` → an array (and if you mix that with other nodes, an array containing a nested array). So `props.children.map(...)` throws a `TypeError` for the single-child case, and `props.children.length` silently means "number of characters" when the child is a string. Treat the value as opaque: render it, pass it along, or use React's `Children` helpers if you truly must walk it. ## What actually renders Whatever ends up in `children` follows the normal rules for renderable nodes. Elements, strings, numbers and arrays render. `null`, `undefined` and booleans render nothing at all — which is why `{isOpen && <Dialog />}` is safe to nest. This means a container can receive a child that produces no output, and it cannot tell the difference from the outside. ## Elements are descriptions, not DOM A React element you receive as `children` is an immutable plain object describing what to render — a type plus props. It is not a DOM node and not a rendered component. You cannot read its text content, measure it, or mutate its props. Crucially, the child component's function body has **not** run yet: creating `<Chart />` only builds the description; React calls `Chart` when it renders that element in the tree. That is why passing elements around is cheap. ## Why containers matter: containment over configuration A component that renders `{children}` is a *container*: it owns the frame and the caller owns the contents. Compare two designs for a modal: ```jsx // configuration: the modal must anticipate every body it will ever show <Modal title="Delete?" bodyText="..." showIcon iconName="warn" listItems={items} /> // containment: the modal owns the frame, the caller owns the content <Modal title="Delete?"> <WarningIcon /> <p>This cannot be undone.</p> </Modal> ``` The configuration version grows a new prop every time a new use case appears, and every prop is a branch inside the modal. The containment version never changes. This is React's answer to "how do I extend a component?" — you do not subclass it, you nest content inside it. A component that takes `children` and does not inspect it works with content that did not exist when it was written. ## Practical rules - Render `{children}`; do not index, slice, or count it without the `Children` helpers. - If your container needs several regions, do not try to split `children` by position — give each region its own prop and keep `children` for the primary one. - Do not require a specific child type unless you are deliberately building a stricter API; a container that accepts any node is the one that survives refactors. - Remember the shape rule when writing tests or guards: `children == null` is the reliable "nothing was passed" check, not `children.length === 0`.
- If a component receives `children` but never writes `{children}` in its JSX, what does the user see?Nothing from the nested content. There is no implicit slot — React passes the value in props and stops there. The elements are created (they are cheap description objects) but never rendered, so the child components' function bodies never run and no DOM appears. Forgetting `{children}` in a new wrapper component is one of the most common "my content vanished" bugs.
- Does writing `<ExpensiveTree />` as a child mean that component runs even if the parent hides it?No. `<ExpensiveTree />` evaluates to a small object describing what to render; React only calls the `ExpensiveTree` function when that element is actually placed in the rendered tree. If the container returns `null` or omits `{children}`, the child never executes. Creating elements you might not render is cheap by design.
- How do you check whether a caller passed any content at all?Compare against null: `if (children == null) return null;` covers both `undefined` (nothing nested) and an explicitly passed `null`. Avoid `children.length`, which is character count for a string child and throws for a single element. If you need a real node count across all shapes, `Children.count(children)` normalizes it.
saying these in an interview costs you the question
- Says props.children is always an array
- Calls children.length to count nested nodes
- Thinks children is a DOM node you can read
- Believes children must be declared or registered somehow
- Assumes nested elements render even without {children}