Children

Provides helpers for counting, mapping and flattening the opaque children prop.

Signature
Children.count(children)
Children.forEach(children, fn, thisArg?)
Children.map(children, fn, thisArg?)
Children.only(children)
Children.toArray(children)

Parameters

ParameterTypeDescription
$childrenReact.ReactNodeThe value of the `children` prop your component received. Every method takes this as its first argument.
$fn(child: ReactNode, index: number) => ReactNodeCallback for map and forEach, called with each child and a zero-based index. For map it must return a React node.
$thisArgoptionalany= undefinedThe `this` value for fn in map and forEach.

Return value

number | Array<ReactNode> | ReactElement | undefinedDepends on the method: `count` returns a number, `map` and `toArray` return a flat array of nodes, `only` returns the single element, `forEach` returns undefined. `map` returns `children` unchanged when it is null or undefined.

Errors

Children.only throws if children is not exactly one React element — including when it is an array containing a single element.

The children prop is deliberately opaque. It may be a single element, a string, a number, false, an array, or nested arrays from a .map(), so children.length and children.map() are wrong roughly as often as they are right. These helpers normalise all of that.

When to reach for it

Chiefly in older class-based code, and in generic layout components that must count or wrap whatever they are given — a list that numbers its rows, a toolbar that inserts separators, a wrapper that enforces exactly one child. Children.map also merges the keys it generates with the caller's keys, so reordered children keep their identity; a hand-rolled loop does not do that.

What trips people up

Traversal is one level deep and stops at element boundaries. A <Fragment> child counts as one item and its contents are never visited, so wrapping three rows in a fragment turns Children.count from 3 into 1. You also cannot see what a child renders — only the element describing it.

React itself calls this API fragile. Prefer an explicit array prop, several named slots, or a render prop when you control the component's API.

Example

Try it yourself
Loading editor…
Loading React…

When to use it

  • Number or separate the rows of a generic list component
  • Refuse to render unless exactly one child element was passed
  • Add an index prop to every child in older class-based code

Frequently asked questions

How do I loop over props.children in React?

Use Children.map(children, fn) rather than children.map, because children may be a single element, a string, or nested arrays rather than a flat array.

Why does Children.count return 1 for a Fragment with several elements inside?

Traversal never descends into an element, and a Fragment is an element. The children inside it belong to the Fragment's own props, so they are not counted.

What is the difference between Children.toArray and Children.map?

toArray flattens children into an array and drops null, undefined and booleans without transforming anything, while map also applies your callback and merges the keys it produces with the original ones.

More examples

Inserting separators between children

Renders `Home / Docs / Children` with a separator after every child except the last. The count is needed up front because the callback has no other way to know which item is final.

Example · jsx
import { Children } from 'react';

function Breadcrumbs({ children }) {
  const last = Children.count(children) - 1;

  return (
    <p>
      {Children.map(children, (child, i) => (
        <>
          {child}
          {i < last ? ' / ' : ''}
        </>
      ))}
    </p>
  );
}

export default function App() {
  return (
    <Breadcrumbs>
      <a href="/">Home</a>
      <a href="/docs">Docs</a>
      <span>Children</span>
    </Breadcrumbs>
  );
}

Requiring exactly one child

The button renders with a title attribute. Adding a second child, or wrapping the button in an array, makes Children.only throw immediately with a clear error rather than failing later inside cloneElement.

Example · jsx
import { Children, cloneElement } from 'react';

function Tooltip({ label, children }) {
  // Throws if given zero, two, or an array of children
  const child = Children.only(children);
  return cloneElement(child, { title: label });
}

export default function App() {
  return (
    <div>
      <Tooltip label="Saves your work">
        <button>Save</button>
      </Tooltip>
    </div>
  );
}

Why the array prop is usually better

FragileRows reports 1 row because the Fragment counts as a single child, while Rows correctly reports 2. This is the failure mode that makes React recommend an explicit data prop over children introspection.

Example · jsx
// Fragile: breaks as soon as a caller wraps children in a Fragment,
// a conditional, or another component.
import { Children } from 'react';

function FragileRows({ children }) {
  return <p>{Children.count(children)} rows</p>;
}

// Robust: the data is explicit and cannot be hidden by markup.
function Rows({ items }) {
  return (
    <div>
      <p>{items.length} rows</p>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{item.label}</li>
        ))}
      </ul>
    </div>
  );
}

export default function App() {
  const items = [
    { id: 'a', label: 'Alpha' },
    { id: 'b', label: 'Beta' },
  ];

  return (
    <div>
      <FragileRows>
        <>
          <span>Alpha</span>
          <span>Beta</span>
        </>
      </FragileRows>
      <Rows items={items} />
    </div>
  );
}

Discussion

  • Be the first to comment on this lesson.