Question

How to Type the children Prop in React with TypeScript

typescriptreactjsjsxtypescript2.0

Question

I have a simple React functional component in TypeScript:

import * as React from 'react';

export interface AuxProps {
  children: React.ReactNode;
}

const Aux = (props: AuxProps) => props.children;

export default Aux;

And another component:

import * as React from 'react';

export interface LayoutProps {
  children: React.ReactNode;
}

const Layout = (props: LayoutProps) => (
  <Aux>
    <div>Toolbar, SideDrawer, Backdrop</div>
    <main>{props.children}</main>
  <Aux/>
);

export default Layout;

I get this TypeScript error:

JSX element type 'ReactNode' is not a constructor function for JSX elements.
Type 'undefined' is not assignable to type 'ElementClass'. [2605]

How should children be typed correctly in React with TypeScript, and why does this error happen?

Short Answer

By the end of this page, you will understand what the children prop represents in React, when to use React.ReactNode, why a component cannot directly return just any ReactNode type in some TypeScript setups, and how to type wrapper components correctly using React.ReactNode, JSX.Element, or React.FC patterns.

Concept

What children means in React

In React, children is the content placed between a component's opening and closing tags.

<Aux>
  <div>Hello</div>
</Aux>

Here, the <div>Hello</div> part becomes the children prop of Aux.

The correct type for children

In TypeScript, the most common type for children is:

React.ReactNode

React.ReactNode is a broad type that includes:

  • React elements
  • strings
  • numbers
  • fragments
  • arrays of nodes
  • null
  • undefined
  • booleans

That makes it a good type for children, because React allows all of these to be rendered.

Why the error happens

Your children prop type is actually fine. The real issue is the component return type and JSX structure.

This component:

const Aux = (props: AuxProps) => props.children;

returns props.children, which is typed as React.ReactNode.

But in some TypeScript and React type setups, a function used as a JSX component is expected to return a JSX-compatible element type such as:

  • JSX.Element
  • React.ReactElement
  • null

Since ReactNode is wider than that and may include undefined, TypeScript complains.

There is also a second issue in your Layout component:

<Aux/>

This is a self-closing tag, but you intended to close the opening <Aux> tag. It should be:

</Aux>

So the problem is not that children is incorrectly typed. The main problems are:

  1. Aux returns ReactNode, which may be too broad for JSX component typing in your setup.
  2. The closing tag is incorrect in Layout.

Why this matters

Typing children correctly is important because many real components are wrappers:

  • layouts
  • modals
  • cards
  • providers
  • form sections

These components usually receive other elements as content. If the type is too narrow, valid React usage breaks. If the component return type is too broad, TypeScript may reject it as a JSX component.

Mental Model

Think of children as the contents of a box.

  • The component is the box.
  • Anything placed inside the box becomes children.
  • React.ReactNode means "almost anything React knows how to display can go in this box."

Now think about the component's return value as what the factory is allowed to ship out.

  • children can be many different things.
  • But a JSX component is usually expected to return a proper React element or null.
  • If TypeScript sees that your component might return undefined, it says, "This is not a reliable JSX component."

So:

  • children type answers: What can go inside?
  • return type answers: What does the component produce?
Take Quiz

Syntax and Examples

Basic children prop typing

type Props = {
  children: React.ReactNode;
};

function Wrapper({ children }: Props) {
  return <div>{children}</div>;
}

This is the most common pattern.

Corrected version of your example

import * as React from 'react';

type AuxProps = {
  children: React.ReactNode;
};

const Aux = ({ children }: AuxProps): JSX.Element => {
  return <>{children}</>;
};

export default Aux;

Why this works

  • children is still typed as React.ReactNode
  • the component returns a fragment: <>...</>
  • a fragment is a valid JSX element
  • TypeScript is happy because the component clearly returns a JSX element

Corrected Layout component

import * as React from 'react';
import Aux from './Aux';

type LayoutProps = {
  children: React.ReactNode;
};

const Layout = ({ children }: LayoutProps): JSX.Element => {
  return (
    <Aux>
      <div>Toolbar, SideDrawer, Backdrop</div>
      <main>{children}</main>
    </Aux>
  );
};

export default Layout;

Another common option: PropsWithChildren

React provides a helper type:

import * as React from 'react';

type LayoutProps = React.PropsWithChildren<{}>;

function Layout({ children }: LayoutProps) {
  return <main>{children}</main>;
}

If your component has other props:

type CardProps = React.PropsWithChildren<{
  title: string;
}>;

function Card({ title, children }: CardProps) {
  return (
    <section>
      <h2>{title}</h2>
      {children}
    </section>
  );
}

Using React.FC

const Aux: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  return <>{children}</>;
};

This works too, but many teams prefer explicit props typing without React.FC for clarity and flexibility.

Step by Step Execution

Trace through a small example

type BoxProps = {
  children: React.ReactNode;
};

function Box({ children }: BoxProps): JSX.Element {
  return <section>{children}</section>;
}

function App(): JSX.Element {
  return (
    <Box>
      <p>Hello</p>
    </Box>
  );
}

What happens step by step

1. App renders

React sees:

<Box>
  <p>Hello</p>
</Box>

2. The inner content becomes children

This is roughly like calling:

Box({ children: <p>Hello</p> })

3. BoxProps validates the input

TypeScript checks whether <p>Hello</p> fits React.ReactNode.

It does, because a React element is a valid ReactNode.

4. Box returns JSX

return <section>{children}</section>;

This produces a valid JSX element.

5. React renders the final output

The result is effectively:

<section>
  <p>Hello</p>
</section>

Why your original Aux caused trouble

With this version:

const Aux = (props: AuxProps) => props.children;

TypeScript infers the return type as React.ReactNode.

Because ReactNode may be undefined, the component is not always seen as a valid JSX component in stricter or older type definitions.

Wrapping the result in a fragment fixes that:

const Aux = ({ children }: AuxProps): JSX.Element => <>{children}</>;

Real World Use Cases

Layout wrappers

A layout component often wraps pages and renders shared UI around page content.

<Layout>
  <Dashboard />
</Layout>

Modal components

A modal usually accepts any content as children.

<Modal>
  <form>...</form>
</Modal>

Card and panel components

Reusable UI containers often render children inside a styled box.

<Card>
  <p>Profile details</p>
</Card>

Context providers

Providers typically wrap part of the app and render descendant components through children.

<AuthProvider>
  <App />
</AuthProvider>

Form sections and reusable shells

Components like Page, SidebarLayout, ErrorBoundary, and TabsPanel all commonly depend on correctly typed children.

Take Quiz

Real Codebase Usage

Common patterns developers use

Wrapper components

The most common pattern is a wrapper that accepts children and returns JSX:

type Props = {
  children: React.ReactNode;
};

function Container({ children }: Props) {
  return <div className="container">{children}</div>;
}

Guard clauses

Sometimes a component only renders when children exists.

function MaybeWrap({ children }: { children?: React.ReactNode }) {
  if (!children) return null;
  return <div>{children}</div>;
}

Validation and fallback UI

function Panel({ children }: { children?: React.ReactNode }) {
  return <section>{children ?? <p>No content</p>}</section>;
}

Composition

React codebases rely heavily on composition rather than inheritance. children is a key part of that.

function Page({ children }: { children: React.ReactNode }) {
  return (
    <div>
      <header>Site Header</header>
      <main>{children}</main>
    </div>
  );
}

PropsWithChildren in shared types

In larger projects, developers often use helper types to reduce repetition.

type PanelProps = React.PropsWithChildren<{
  title: string;
}>;

This keeps prop definitions concise when many components accept children.

Common Mistakes

1. Returning children directly when the inferred type is too broad

Broken example:

const Aux = (props: { children: React.ReactNode }) => props.children;

Why it can fail:

  • TypeScript may infer the return type as React.ReactNode
  • ReactNode can include undefined
  • JSX components are often expected to return JSX.Element or null

Fix:

const Aux = ({ children }: { children: React.ReactNode }): JSX.Element => {
  return <>{children}</>;
};

2. Mistyping the closing tag

Broken example:

<Aux>
  <main>{children}</main>
<Aux/>

Fix:

<Aux>
  <main>{children}</main>
</Aux>

3. Typing children too narrowly

Broken example:

type Props = {
  children: JSX.Element;
};

Why this is limiting:

  • strings like "Hello" would fail
  • multiple elements in an array would fail
  • null would fail

Better:

children: React.ReactNode;

4. Forgetting that children can be optional

Some components do not require content.

type Props = {
  children?: React.ReactNode;
};

Use ? when the component should work without inner content.

5. Confusing ReactNode with ReactElement

  • ReactNode = anything React can render
  • ReactElement = a React element object only

If you want flexibility, use ReactNode for children.

6. Using lowercase component names

Broken example:

const aux = () => <div />;

In JSX, lowercase names are treated like HTML tags, not React components.

Better:

const Aux = () => <div />;

Comparisons

Related type choices

TypeWhat it meansGood for children?Notes
React.ReactNodeAnything React can renderYesMost common choice
JSX.ElementA single JSX elementSometimesToo narrow for many children cases
React.ReactElementA React element objectSometimesSimilar to JSX.Element, still narrower than ReactNode
stringText onlyNoToo restrictive
anyAnything without checkingNoAvoids type safety

ReactNode vs JSX.Element

children: React.ReactNode;

Use this when content may be:

  • text
  • elements
  • fragments
  • arrays
  • null
children: JSX.Element;

Use this only when you want exactly one JSX element.

Explicit props vs React.FC

ApproachExampleProsCons
Explicit propsfunction Comp({ children }: Props)Clear and flexibleSlightly more typing
React.FCconst Comp: React.FC<Props> = ...ConvenientSome teams avoid it for style and clarity reasons

Returning children directly vs wrapping in a fragment

Return styleExampleSafe in JSX component typing?
Direct returnreturn children;Sometimes problematic
Fragmentreturn <>{children}</>;Yes
Wrapper elementreturn <div>{children}</div>;Yes
Take Quiz

Cheat Sheet

Quick reference

Type children

type Props = {
  children: React.ReactNode;
};

Optional children

type Props = {
  children?: React.ReactNode;
};

Safe wrapper component

function Wrapper({ children }: { children: React.ReactNode }): JSX.Element {
  return <>{children}</>;
}

With other props

type CardProps = {
  title: string;
  children: React.ReactNode;
};

With PropsWithChildren

type Props = React.PropsWithChildren<{
  title: string;
}>;

Key rules

  • Use React.ReactNode for most children props
  • Return JSX or null from a component
  • If returning only children, wrap it in a fragment when needed
  • Use uppercase names for React components
  • Use children? only if the content is optional

Common fix for this exact issue

const Aux = ({ children }: { children: React.ReactNode }): JSX.Element => {
  return <>{children}</>;
};

FAQ

What is the correct TypeScript type for React children?

Usually React.ReactNode. It covers most values React can render, including elements, strings, numbers, fragments, null, and arrays.

Why does returning props.children directly cause an error?

Because children is typed as ReactNode, and that type can include undefined. Some TypeScript React setups require JSX components to return a JSX element or null.

Should I use JSX.Element for children?

Usually no. JSX.Element is narrower and only allows a single JSX element. React.ReactNode is more flexible.

Is React.FC required to type children?

No. You can type props explicitly. React.FC is optional.

When should children be optional?

Make it optional when the component should still work without inner content.

children?: React.ReactNode;

What is the difference between ReactNode and ReactElement?

ReactNode is anything React can render. ReactElement is specifically a React element object.

Was the closing tag also part of the problem?

Yes. In your example, <Aux/> should have been </Aux> to close the wrapper component properly.

Take Quiz

Mini Project

Description

Build a reusable Card wrapper component that accepts a title and displays any nested content using the children prop. This demonstrates the most common real-world use of children: creating reusable layout and UI wrapper components.

Goal

Create a typed React component that safely accepts children and renders it inside a styled wrapper.

Requirements

  1. Create a Card component with a required title prop.
  2. Type the nested content using React.ReactNode.
  3. Return valid JSX from the component.
  4. Render the component with different kinds of children, such as text and elements.
Take Quiz

Keep learning

Related questions

View more