# Understanding React children

The `children` prop lets a component render the JSX placed between its opening
and closing tags. React also provides a `Children` API for the less common cases
where a component needs to inspect or change that JSX.

## The `children` prop

`children` is a normal prop. React fills it with the content between a
component's tags:

```tsx
import { type ReactNode } from 'react'

interface ListProps {
  children: ReactNode
}

function List({ children }: ListProps) {
  return <ul>{children}</ul>
}

function App() {
  return (
    <List>
      <li>Apple</li>
      <li>Banana</li>
    </List>
  )
}
```

Here, the two `<li>` elements become the `children` value passed to `List`.
`ReactNode` is the usual TypeScript type when a prop may contain anything React
can render, such as JSX, text, a number, `null`, or several of those values.

## Render a list from data when you can

If your items already live in an array, use the array's `map` method. This is
the simplest way to get each item's index:

```tsx
import { type ReactNode } from 'react'

interface ListItemProps {
  children: ReactNode
  index: number
}

function ListItem({ children, index }: ListItemProps) {
  const color = index % 2 === 0 ? 'red' : 'blue'

  return (
    <li style={{ color }}>
      {children} {index}
    </li>
  )
}

function App() {
  const items = ['foo', 'bar', 'baz']

  return (
    <ul>
      {items.map((item, index) => (
        <ListItem key={item} index={index}>
          {item}
        </ListItem>
      ))}
    </ul>
  )
}
```

The result looks like this:

[Interactive example](/children)

This approach is clear because the item, key, and index are all passed in one
place. For a complete example that filters an array while rendering a React
list, see [the filterable list component](/filterable-list).

## When `Children.map` is useful

Sometimes an API is easier to read when callers write each item as JSX:

```tsx
<List>
  <ListItem>foo</ListItem>
  <ListItem>bar</ListItem>
  <ListItem>baz</ListItem>
</List>
```

The `children` value may look like an array here, but React does not promise
that it is one. One child, many children, and no children can have different
shapes. Use `Children.map` when you need to visit each child safely.

The next example adds an `index` prop to each child. `cloneElement` creates a
new element with that extra prop; it does not change the original element.

```tsx
import { Children, cloneElement, isValidElement, type ReactNode } from 'react'

interface ListItemProps {
  children: ReactNode
  index?: number
}

function ListItem({ children, index }: ListItemProps) {
  const color = index !== undefined && index % 2 === 0 ? 'red' : 'blue'

  return (
    <li style={{ color }}>
      {children} {index}
    </li>
  )
}

interface ListProps {
  children: ReactNode
}

function List({ children }: ListProps) {
  const indexedChildren = Children.map(children, (child, index) => {
    if (!isValidElement<ListItemProps>(child)) return child

    return cloneElement(child, { index })
  })

  return <ul>{indexedChildren}</ul>
}
```

`isValidElement` is important. A React child can be text, `null`, or another
renderable value that `cloneElement` cannot clone.

The JSX-only version produces the same result:

[Interactive example](/children)

## The five `Children` methods

- `Children.map(children, fn)` returns a new list after running `fn` for each
  child
- `Children.forEach(children, fn)` visits each child but does not return a list
- `Children.count(children)` returns the number of children React sees
- `Children.only(children)` returns one React element and throws an error if it
  receives anything else
- `Children.toArray(children)` returns a flat array that you can filter, sort,
  or reverse

These methods only inspect the JSX passed to the component. They do not render
another component and look inside its result. For example, if `<MoreItems />`
renders three list items, `Children.count` still sees `<MoreItems />` as one
child.

Fragments are another sharp edge: `Children` does not visit the items inside a
Fragment. These limits can make code based on the exact number or order of
children surprising.

## Avoid matching children by component type

It is possible to compare `child.type` with a component:

```tsx
if (isValidElement(child) && child.type === ListItem) {
  // This is a ListItem element in this exact form
}
```

This check is easy to break. It may stop matching after the component is wrapped
by another component. It also cannot see components returned from inside another
child. TypeScript checks the code you write, but it does not fix these limits at
runtime.

Adding a private prop such as `__TYPE` has the same problem and exposes an
implementation detail to every caller. Prefer an explicit prop or a different
component API instead.

## Prefer explicit data for anything complex

`Children` and `cloneElement` are still part of React, but the React docs call
them uncommon and warn that they can lead to fragile code. If a component needs
to filter items, reorder them, attach several values, or recognize different
item types, pass structured data instead:

```tsx
interface Item {
  id: string
  label: string
}

interface ListProps {
  items: Item[]
}

function List({ items }: ListProps) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={item.id}>
          {item.label} {index}
        </li>
      ))}
    </ul>
  )
}
```

Now `items` is a real array, each item has a clear shape, and the data flow is
easy to follow. Context or a render prop can also be a better fit when nested
components need information from their parent.

Use the lowercase `children` prop freely. Reach for the uppercase `Children` API
only when a component truly needs to work with the JSX it receives, and use
`cloneElement` sparingly.

For the full details, see the React reference pages for
[`Children`](https://react.dev/reference/react/Children) and
[`cloneElement`](https://react.dev/reference/react/cloneElement).
