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:
- Adding an explicit return type to the component function (you’ll notice in the above example I chose to omit a manual return type).
- Adding type-checking to a handful of static properties like
displayName
anddefaultProps
.
// @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.