Editor’s note: This article was last updated for code clarity and to add information about supported children types like ReactElement
and JSX.Element
on 20 October 2023.
Typing the children
prop in React can be challenging at first. If you try typing them as specific JSX types, you may run into issues rendering the child components. There is also the problem with the paradox of choice, as there are multiple available options to type the children
prop — this may lead to decision fatigue.
In this article, I’ll provide recommended solutions to effectively type the children
prop, addressing common issues and avoiding decision fatigue.
When you write a JSX expression with opening and closing tags, the content passed between them is referred to as their child:
Consider the following example:
<Border> Hey, I represent the JSX children! </Border>
In this example, the literal string Hey, I represent the JSX children!
refers to the child rendered within Border
.
Meanwhile, to gain access to the content passed between JSX closing and opening tags, React passes these in a special prop: props.children
. For example, Border
could receive the children
prop as follows:
const Border = ({children}) => { return <div style={{border: "1px solid red"}}> {children} </div> }
Border
accepts the children
prop, then renders children
within a div
with a border style of 1px solid red
.
This is the basic usage of the children
prop — to receive and manipulate the content passed within the opening and closing tags of a JSX expression.
Strictly speaking, there’s a handful of supported content types that can go within the opening and closing tags of your JSX expression. Below are some of the most commonly used ones.
Literal strings are valid children types. The example below shows how to pass one into a component:
<YourComponent> This is a valid child string </YourComponent />
Note that in YourComponent
, props.children
will be the string This is a valid child string
.
You can also pass other JSX elements as valid children types. This is usually helpful when composing different nested components. Below is an example:
<Wrapper> <YourFirstComponent /> <YourSecondComponent /> </Wrapper>
It is also completely acceptable to mix children types, as seen here:
<Wrapper> I am a valid string child <YourFirstComponent /> <YourSecondComponent /> </Wrapper>
Expressions are equally valid children types. As shown below, myScopedVariableReference
can be any JavaScript expression:
<YourFirstComponent> {myScopedVariableReference} </YourFirstComponent>
Remember that expressions in JSX
are written in curly braces.
Functions are equally valid children types as shown below:
<YourFirstComponent> {() => <div>{myScopedVariableReference}</div>} </YourFirstComponent>
As you can see, the children
prop can be represented by a large range of data types. Your first inclination might be to type these out manually, like so:
type Props = { children: string | JSX.Element | JSX.Element[] | () => JSX.Element } const YourComponent = ({children} : Props) => { return children }
But this doesn’t fully represent the children
prop. What about fragments, portals, and ignored render values, such as undefined
, null
, true
, or false
?
A full representation may look something like this:
type ReactText = string | number; type ReactChild = ReactElement | ReactText; interface ReactNodeArray extends Array<ReactNode> {} type ReactFragment = {} | ReactNodeArray; type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined; type Props = { children: ReactNode } // source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d076add9f29db350a19bd94c37b197729cc02f87/types/react/index.d.ts
See the extended types for ReactPortal
and ReactElement
. Do they look complex? There’s a good chance they do.
The point I’m trying to make is, in practice, you don’t want to manually type the children
prop. Instead, I suggest using the officially supported types discussed below.
PropsWithChildren
typeThe React.PropsWithChildren
type takes your component prop and returns a union type with the children
prop appropriately typed. No extra work from you is needed.
In practice, here’s the definition of the PropsWithChildren
type:
type PropsWithChildren<P> = P & { children?: ReactNode };
Assume you had a component Foo
with FooProps
:
type FooProps = { name: 'foo' } export const Foo = (props: FooProps) => { return null }
You can introduce the children
prop as follows:
import { PropsWithChildren } from 'react' type FooProps = { name: 'foo' } export const Foo = (props: PropsWithChildren<FooProps>) => { return props.children }
When you pass PropsWithChildren
to FooProps
, you get the children
prop intern
ally typed.
In most cases, this is the recommended way to type the children
prop because it requires less boilerplate, and the children
prop is implicitly typed.
ReactNode
typeIn cases where you must explicitly type the children
prop, you can use the ReactNode
type.
Remember the definition for the PropsWithChildren
type:
type PropsWithChildren<P> = P & { children?: ReactNode };
Instead of relying on PropsWithChildren
, you can also type the children
prop directly:
import { ReactNode } from 'react' type FooProps = { name: 'foo' // look here 👇 children: ReactNode } export const Foo = (props: FooProps) => { return props.children }
FunctionComponent
(or FC
) typeThe FunctionComponent
generic interface may also be used to appropriately type the children
prop. Internally, this interface relies on PropsWithChildren
.
Here’s how you’d use it:
import { FunctionComponent, ReactElement } from 'react' interface FooProps extends React.PropsWithChildren { name: 'foo', children: ReactElement } export const Foo: FunctionComponent<FooProps> = (props) => { return props.children }
Note that the FC
type is an alias for FunctionComponent
for ease. Their usages are similar, as shown below:
import { FC, ReactElement } from 'react' interface FooProps extends React.PropsWithChildren { name: 'foo', children: ReactElement } export const Foo: FC<FooProps> = (props) => { return props.children }
Component
type for class componentsMost modern React codebases no longer use class components, except in specific use cases. If you find yourself needing to type the children
prop in a class component, leverage the Component
prop, as shown below:
import { Component } from 'react' type FooProps = { name: 'foo' } class Foo extends Component<FooProps> { render() { return this.props.children } }
The Component
type automatically includes the children
prop.
ReactElement
You can also use the ReactElement
interface to appropriately type the children
prop. ReactElement
is a subset of ReactNode
:
import { ReactElement } from 'react' type Props = { name: 'foo' } export const Foo = (props: { children: ReactElement<Props>}) => { return props.children }
JSX.Element
JSX.Element
can also be an appropriate type for the children
prop. Below is an example showing how to implement it:
import { JSX } from "react"; export const Foot = (props: { children: JSX.Element }) => { return props.children; };
In this article, we learned the best practices for typing children
props in TypeScript in both functional and class-based components. Where possible, it is recommended to use the PropsWithChildren
type as opposed to manually typing the children
props or using any other approach listed.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowNitro.js is a solution in the server-side JavaScript landscape that offers features like universal deployment, auto-imports, and file-based routing.
Ding! You got a notification, but does it cause a little bump of dopamine or a slow drag of cortisol? […]
A guide for using JWT authentication to prevent basic security issues while understanding the shortcomings of JWTs.
Auth.js makes adding authentication to web apps easier and more secure. Let’s discuss why you should use it in your projects.
2 Replies to "Using the React children prop with TypeScript"
Thankyou for this resource
FYI, since React 18, the FC type no longer contains the children prop.