What lies behind the children prop in React and TypeScript?
\n\nIf you've ever written a React component using TypeScript, you've likely encountered the question: "What data type is the children prop?". This question is one of the most popular on Stack Overflow, and for good reason. At first glance, the answer seems simple: ReactNode. But as they say, the devil is in the details.
In this article, we won't just give a dry answer. We'll break down all possible types for children, their differences, best practices, and pitfalls. You'll learn when to use ReactNode, when to use ReactElement, and when to use more specific types. Let's go!
Why is this question so popular?
\n\nThe problem is that children is a special prop in React. It can be anything:
- \n
- A string:
"Hello, world!"\n - A number:
{42}\n - A JSX element:
<div>...</div>\n - An array of elements:
[<span/>, <span/>]\n - A function (render prop):
{() => <div/>}\n - Even
null,undefined, orboolean\n
Because of this flexibility, beginners often write children: any, which negates all the benefits of TypeScript. Let's figure out the correct approaches.
Main types for children
\n\n1. ReactNode — the universal soldier
\n\nThis is the most common and correct type for children in 90% of cases. It includes everything that React can render.
import { ReactNode } from 'react';\n\ninterface CardProps {\n title: string;\n children: ReactNode;\n}\n\nfunction Card({ title, children }: CardProps) {\n return (\n <div className="card">\n <h2>{title}</h2>\n <div className="card-content">\n {children}\n </div>\n </div>\n );\n}\n\n// Usage:\n<Card title="My Card">\n <p>This text will be rendered</p>\n {123} {/* Numbers also work */}\n {null} {/* And null */}\n</Card>\n\nWhat is included in ReactNode? It is essentially a union of:
\n- \n
ReactElement— JSX elements \n stringandnumber— primitives \n boolean,null,undefined— ignored during rendering \n ReactNode[]— an array of the above \n ReactPortal— portals \n
2. ReactElement — strict mode
\n\nIf you need children to be only a JSX element (not a string or number), use ReactElement.
import { ReactElement } from 'react';\n\ninterface StrictCardProps {\n children: ReactElement; // Only one JSX element\n}\n\nfunction StrictCard({ children }: StrictCardProps) {\n return <div className="strict-card">{children}</div>;\n}\n\n// Works:\n<StrictCard><span>Only an element</span></StrictCard>\n\n// TypeScript error:\n<StrictCard>Text</StrictCard> // ❌ String is not allowed\n\nImportant: ReactElement requires exactly one child element. If you pass two elements without a wrapper, TypeScript will also throw an error.
3. JSX.Element — an alias for ReactElement
\n\nThe type JSX.Element is simply ReactElement<any, any>. It is less flexible than ReactNode and is generally not recommended for children unless you are writing a library.
// Essentially, these are the same:\ntype A = JSX.Element; // ReactElement<any, any>\ntype B = ReactElement; // Re