Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

React Children with TypeScript

September 23, 2020
reacttypescript

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.

React Children 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:

Class children

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.
Click here to share this post on Twitter

If you to learn more about using TypeScript with React, you may find my course useful:

Using TypeScript with React

Using TypeScript with React
Find out more

Want more content like this?

Subscribe to receive notifications on new blog posts and courses

Required
© Carl Rippon
Privacy Policy