Defining React Children in Functional Components with Typescript

React | Typescript

If you’re using React with Typescript and looking to create a functional component that includes React children as a prop, you might find yourself wondering exactly how to include said children in your props type definition. There’s actually a few different ways to do this, each with its own relative merits. In this article I’ll demonstrate 3 common approaches and attempt to summarise the pros and cons of each one.

Include children in Props Type Definition Directly

The first approach, and probably the most obvious, is to simply include children in your standard props type definition.

interface Props {
  children: React.ReactNode;
  myOtherProp: string;
}

const MyComponent = ({ children, myOtherProp }: Props): JSX.Element => ...

As you can see, we’ve included children alongside our original props and typed it as a React.ReactNode, which is how React types children internally.

// @types/react/index.d.ts
type ReactNode =
  | ReactChild
  | ReactFragment
  | ReactPortal
  | boolean
  | null
  | undefined;

Use React.PropsWithChildren

If you don’t want to include children manually, you could opt to use React’s PropsWithChildren generic type.

type Props = React.PropsWithChildren<{ myOtherProp: string }>;

const MyComponent = ({ children, myOtherProp }: Props): JSX.Element => ...

This time, we provide our props definition as a type argument to PropsWithChildren, resulting in an intersection type that automatically includes children. Note that, by this definition, children would be considered optional.

// @types/react/index.d.ts
type PropsWithChildren<P> = P & { children?: ReactNode };

Use React.FunctionComponent

The final option we’ll look at it is using React’s FunctionComponent type (or the more commonly used FC alias). Unlike PropsWithChildren, we’ll use this to type the component function itself, rather than the props directly.

const MyComponent: React.FC<{ myOtherProp: string }> = ({ children, myOtherProp }) => ...

Similarly to PropsWithChildren we passed our original props as a type argument to FC. This will configure the function’s props parameter type to include myOtherProp but also, once again, automatically include children. Note that, this actually uses PropsWithChildren under the hood, which also means that, as before, children will be optional. The main difference with PropsWithChildren is that FC will also provide other additional type information to our functional component as a whole. This includes:

// @types/react/index.d.ts
interface FunctionComponent<P = {}> {
  (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
  propTypes?: WeakValidationMap<P>;
  contextTypes?: ValidationMap<any>;
  defaultProps?: Partial<P>;
  displayName?: string;
}

For example, if I try to set displayName on MyComponent to anything other than a string (or undefined), a previously unseen type error will now occur.

// Error: Type '123' is not assignable to type 'string | undefined'.
MyComponent.displayName = 123;

Which Approach to Use?

So, which one should you actually use? Looking at the additional information React.FC provides you might be tempted into thinking that this is the best option. In fact, it might seem like this should be used in every component, regardless of whether children are required. Whilst this is a perfectly valid assumption, it’s worth pointing out that, though perhaps they won’t be experienced by everyone, React.FC does have some known problems that lead many to think the purported benefits of using React.FC may not actually be worth it. I won’t cover all the issues here, but the main objection is the implicit inclusion of children on every component, even if children aren’t used. Obviously, this isn’t really an issue if you’re planning to use React.FC exclusively for components that do include children. You can find more information about some of the pitfalls of React.FC here and see why create-react-app chose to drop the type from their Typescript template here.

Realistically, any approach will work just fine. If you’ve considered the supposed “downsides” of React.FC and they don’t seem that problematic to you, I’d probably just go with that. Personally, I would only use React.FC if I was willing to use it everywhere (even if children aren’t required) to avoid inconsistencies between return types and static properties in different components. However, due to the issues referenced above, I wouldn’t be prepared to make that commitment, taking React.FC off the table.

If it’s then between including children in my props type definition manually or using React.PropsWithChildren, I’d likely go with the former. Practically, using PropsWithChildren actually makes the code slightly more verbose whilst also suggesting that children is optional, when in all the relevant cases it’s probably required.