Skip to content
React rc jsx 4 min read

The children Prop

Whatever JSX you place between a component’s opening and closing tags is handed to that component as a single special prop called children. This is the foundation of composition in React: instead of components knowing about every piece of content they wrap, they accept arbitrary content and decide where to render it. Mastering children is what lets you build reusable layouts, cards, modals, and wrapper components that stay generic.

What children actually is

When you write <Card>Hello</Card>, React passes the content Hello to Card as props.children. The component reads that prop and renders it wherever it wants. Nothing is rendered automatically — if a component never references children, the nested content disappears.

function Card({ children }) {
  return <div className="card">{children}</div>;
}

export default function App() {
  return (
    <Card>
      <h2>Profile</h2>
      <p>Composition makes components flexible.</p>
    </Card>
  );
}

Here Card does not know or care what it wraps. It only provides a styled container, and the caller fills it with any markup. This separation is why children is the most common composition tool in React.

The shape of children

The value of children changes depending on what you pass. Knowing its shape helps you avoid surprises when iterating or counting elements.

What you passType of children
Nothingundefined
A single text nodestring
One JSX elementa React element object
Multiple elements/nodesan array of nodes
{condition && <X />}element or false/null

Because the type varies, never assume children is always an array. To work with it generically, use the helpers in the react package such as React.Children.map and React.Children.count, which normalize all of these shapes.

import { Children } from "react";

function List({ children }) {
  return (
    <ul>
      {Children.map(children, (child) => (
        <li>{child}</li>
      ))}
    </ul>
  );
}

Layout and wrapper components

The most valuable use of children is building layout shells. A page layout can define structure once — header, sidebar, footer — and slot the unique content in through children.

function PageLayout({ children }) {
  return (
    <div className="layout">
      <header>DevCraftly</header>
      <main>{children}</main>
      <footer>© 2026</footer>
    </div>
  );
}

function HomePage() {
  return (
    <PageLayout>
      <h1>Welcome</h1>
      <p>This content fills the main slot.</p>
    </PageLayout>
  );
}

Reaching for children to wrap content is almost always cleaner than passing JSX through a regular prop. It reads naturally as nesting and keeps your JSX tree shallow.

Named slots with props

children gives you one slot. When you need several distinct regions — say a card header and a card body — pass additional JSX-typed props alongside children.

function Panel({ title, actions, children }) {
  return (
    <section className="panel">
      <div className="panel-head">
        <h3>{title}</h3>
        <div>{actions}</div>
      </div>
      <div className="panel-body">{children}</div>
    </section>
  );
}

function Dashboard() {
  return (
    <Panel title="Reports" actions={<button>Export</button>}>
      <p>Body content goes through children.</p>
    </Panel>
  );
}

Render-by-children composition

children does not have to be static JSX — it can be a function. This “render prop as children” pattern lets a component compute a value and hand it back to the caller, who decides how to render it.

import { useState } from "react";

function MousePosition({ children }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });

  return (
    <div
      style={{ height: 200 }}
      onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}
    >
      {children(pos)}
    </div>
  );
}

function Tracker() {
  return (
    <MousePosition>
      {({ x, y }) => (
        <p>
          Cursor at {x}, {y}
        </p>
      )}
    </MousePosition>
  );
}

Output:

Cursor at 142, 88

While custom hooks have replaced many render-prop use cases, function-as-children remains useful when the rendered markup must live inside the provider’s own DOM subtree.

Typing children in TypeScript

In TypeScript, the modern way to type any renderable content is React.ReactNode. It covers elements, strings, numbers, arrays, fragments, null, and undefined.

import type { ReactNode } from "react";

interface CardProps {
  children: ReactNode;
}

function Card({ children }: CardProps) {
  return <div className="card">{children}</div>;
}

For a function-as-children component, type children as a function returning ReactNode:

import type { ReactNode } from "react";

interface MouseProps {
  children: (pos: { x: number; y: number }) => ReactNode;
}

Avoid the older React.FC typing, which implicitly added a children prop. Modern React requires you to declare children explicitly, which is more honest about whether a component accepts nested content.

Best Practices

  • Always destructure { children } from props so the contract of your component is visible at a glance.
  • Use children for the primary content slot and named JSX props for secondary regions like headers or actions.
  • Reach for React.Children.map / React.Children.count instead of treating children as a plain array — its shape is not guaranteed.
  • Type children as ReactNode in TypeScript, and as a function returning ReactNode for render-by-children.
  • Prefer custom hooks over function-as-children unless the output must render inside the provider’s DOM.
  • Do not mutate or deeply inspect children to drive logic; pass explicit props instead for clearer, more predictable components.
Last updated June 14, 2026
Was this helpful?