React Children with TypeScript
The React children prop allows components to be composed together and is a key concept for building reusable components. Visually, we can think of it as a hole in the component where the consumer controls what is rendered. This post covers different approaches to strongly-typing this powerful and flexible prop with TypeScript.
Using the FC
type
There is a standard React type, FC
, that we can use on arrow function components. FC stands for Function Component, and it aliases a type called FunctionComponent
.
Here’s an example:
type Props = {
title: string,
};
const Page: React.FC<Props> = ({
title,
children,
}) => (
<div>
<h1>{title}</h1>
{children}
</div>
);
FC
is a generic type that takes in the specific type for all the component props. In the example above, we passed in a Props
type alias containing a title
prop. Alternatively, an interface could be used to define Props
.
Notice that children
isn’t defined in Props
. Instead, it is already defined in the FC
type. This is nice if you know this fact, but it might be a bit confusing if you don’t.
Explicitly defining the children
prop type
If we explicitly define the children
prop type, we have several different options for its type.
Let’s go through them one by one …
Using JSX.Element
Let’s try JSX.Element
for starters:
type Props = {
title: string,
children: JSX.Element,};
const Page = ({ title, children }: Props) => (
<div>
<h1>{title}</h1>
{children}
</div>
);
children
is required at the moment. If we want to make this optional for the consumer of the component, we put a question mark(?
) before the type annotation:
type Props = {
title: string;
children?: JSX.Element;};
JSX.Element
is good if the child is required to be a single React element. However, it doesn’t allow multiple children. So, we could make the following adjustment:
type Props = {
title: string;
children?: JSX.Element | JSX.Element[];};
Using ReactChild
A downside of JSX.Element
is that it doesn’t allow strings. So, we could add strings to the union type:
type Props = {
title: string;
children: | JSX.Element | JSX.Element[] | string | string[];};
… but what about numbers?
… this is getting out of hand!
Fortunately, there is a standard type called ReactChild
that includes React elements, strings and numbers. So, we could widen the type for children
as follows:
type Props = {
title: string;
children?: | React.ReactChild | React.ReactChild[];};
Using ReactNode
React.ReactChild | React.ReactChild[]
gives the breadth of values we need, but is a little verbose. ReactNode
is a more terse option:
type Props = {
title: string;
children?: React.ReactNode;};
ReactNode
allows multiple elements, strings, numbers, fragments, portals, …
Perfect!
The FC
generic type uses ReactNode
under the hood as well.
Class components
What if we are using a class component? Let’s explore this:
type Props = {
title: string,
};
export class Page extends React.Component<Props> {
render() {
return (
<div>
<h1>{this.props.title}</h1>
{this.props.children}
</div>
);
}
}
Like FC
, the Component
type automatically includes the children
prop.
If we hover over the children
prop, we discover the type it has been given:
So, the type of the children
prop in a class component is ReactNode
as well.
Wrap up
If we are using function components with the FC
type, the children
prop is already typed. If we explicitly type the children
prop, the ReactNode
is generally the best choice.
Did you find this post useful?
Let me know by sharing it on Twitter.If you to learn more about using TypeScript with React, you may find my course useful: