Typing children, a solution

How do we type this? Well. We have a few choices.

  • JSX.Element;: ðŸ’Đ This doesn't account for arrays (e.g. multiple elements) at all.
  • JSX.Element | JSX.Element[]; 😕 This doesn't accept strings.
  • React.ReactChildren;: ðŸĪŠ Not at even an appropriate type; it's a utility function.
  • React.ReactChild[];: 😀 Accepts multiple children, but it doesn't accept a single child.
  • React.ReactNode;: 🏆 Accepts everything.

This will get everything working as expected:

import React, { ReactNode } from 'react';

type BoxProps = { children: ReactNode };

A helpful utility type

This is a pretty simple utility type that you could write yourself, but you don't have to.

type PropsWithChildren<P = unknown> = P & {
  children?: ReactNode | undefined;
};

It's basically an easy way to say, "This component can render children." That's it. This means, we can refactor our example a little more.

import React, { PropsWithChildren } from 'react';

const Box = ({ children }: PropsWithChildren) => {
  return (
    <section
      className="m-4"
      style={{ padding: '1em', border: '5px solid purple' }}
    >
      {children}
    </section>
  );
};

What if we wanted to add additional props? We can actually pass PropsWithChildren an argument.

import React, { PropsWithChildren } from 'react';

type BoxProps = { color?: 'red' | 'green' | 'blue' };

const Box = ({ children, color = 'red' }: PropsWithChildren<BoxProps>) => {
  return (
    <section
      className="m-4"
      style={{ padding: '1em', border: '5px solid purple', color }}
    >
      {children}
    </section>
  );
};

const Application = () => {
  return (
    <main className="m-8">
      <Box color="green">
        Just a string.
        <p>Some HTML that is not nested.</p>
        <Box>
          <h2>Another React component with one child.</h2>
        </Box>
        <Box>
          <h2 className="mb-4">A nested React component with two children.</h2>
          <p>The second child.</p>
        </Box>
      </Box>
    </main>
  );
};

export default Application;

Alternatively, this will work, but the version that I showed you above is standard practice:

const Box = ({ children, color = 'red' }: PropsWithChildren & BoxProps) => {
  // â€Ķ
};

We'll learn how to make our own utility types like this in a bit.