typescript: typing react components to accept children

2021-09-09

 | 

~2 min read

 | 

356 words

I was working on some React projects recently and wanted to to make sure that my components could accept children - the default behavior.

There are a few different ways to accomplish this, some of which I’ve documented in this CodeSandbox.

Before we walk through the different examples, it’s worth noting that we’re targeting the DefinitelyTyped types for React. These are written by the community, and by looking at the contributors reference at the top of the document, it’s more than just the React core team.

Let’s walk through them.

Implicit Approach: PropsWithChildren

This is my preferred approach for components that we expect to take children. For example, components that are concerned with layouts always receive children and lay them out on the page.

ImplicitComponent.tsx
type Props = React.PropsWithChildren<{
  /* Details go here */
}>
const ImplicitComponent = (props: Props) => {
  /* Do stuff... */
  return <>{props.children}</>
}
import "./styles.css"

type Props = React.PropsWithChildren<{
  /* Details go here */
}>
const ImplicitComponent = (props: Props) => {
  return <>{props.children}</>
}

type WithChildren<T> = T & { children: React.ReactNode }
type AltProps = {
  /* Details go here */
}
const ExplicitComponent = (props: WithChildren<AltProps>) => {
  return <>{props.children}</>
}

type ManualProps = {
  children: React.ReactNode
}
const ManualComponent = (props: ManualProps) => {
  return <>{props.children}</>
}

export default function App() {
  return (
    <div className="App">
      <ImplicitComponent>
        <ExplicitComponent>
          <ManualComponent>
            <h1>Hello CodeSandbox</h1>
            <h2>Start editing to see some magic happen!</h2>
          </ManualComponent>
        </ExplicitComponent>
      </ImplicitComponent>
    </div>
  )
}

Manual Approach

The easiest and most straight forward way to allow children in your components is to simply add them as a property on your component’s interface. For example:

ManualComponent.tsx
type ManualProps = {
  /* Any other details */
  children: React.ReactNode
}
const ManualComponent = (props: ManualProps) => {
  /* Do stuff ... */
  return <>{props.children}</>
}

Note: There does seem to be some inconsistency in the type file of whether children are just ReactNode or ReactNode[], e.g., the type of createElement. I have yet to come across a situation where I’ve needed children to be a list, though it’s worth knowing it’s an option.


Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!