An easier way to add types for "children" in React

Something I had been struggling with since I started using React up until a few months ago was finding a good type to set for the children prop.

The typical route I would take is to sorta just add the types as I needed them to a union like so:

interface Props {
  children: string | undefined | React.ReactChild;
}

which is fine, but then you go to try to render multiple items under the component and you'd get an error:

Screen Shot 2021-03-05 at 11.48.01 AM.png

Essentially saying that you cannot have multiple items as children given the current prop type. So you might go add another type to the union:

interface Props {
  children: string | undefined | React.ReactChild | React.ReactChild[];
}

Ok this works fine now, however it still doesn't handle every use case and will get annoying to repeat over and over again.

PropsWithChildren to the rescue

I accidentally found this type while typing something else out in VSCode and it autocompleted to this.

PropsWithChildren<T> is a generic interface that allows you to define your normal props for your component and then wrapping it in this type so that you can the default children type from react that covers all of the cases.

It's used like so:

import { PropsWithChildren } from 'react'

interface Props {
  title: string;
}

function Card({ title, children }: PropsWithChildren<Props>) {
  return (
    <div>
      <header>
        <h3>{title}</h3>
      </header>
      <div>
        {children}
      </div>
    </div>
  )
}

So first we define our components props, in this case I said the card could take in a title that is a string. Notice how the children type has been removed from the Props interface.

Then at the component level, instead of typing our props argument as just : Props we do PropsWithChildren<Props> which takes our card's props and merges it with the type that contains the children key. Now we can pass whatever as the children to our card component (as long as its a valid react child type) and not have typescript yell at us.

If you prefer to write in inline that could be done as well:

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

function Component({ title, children }: Props) {
  // ...
}

And if you don't need any additional props you can either pass an empty object or record in its place:

function Component({ children }: PropsWithChildren<{}>) {
  // ...
}

// or if your linter is yelling about the empty object type

function Component({ children }: PropsWithChildren<Record<string, unknown>>) {
  // ...
}