You might have come across the issue of needing to pass a React component as a prop in TypeScript and then been faced with a few type errors.
This post will aim to tackle those errors and pave the way so that you can pass a React component as a prop in TypeScript for any of your components, including functional components.
There are a number of ways in which you can pass a component as a prop in React and this post will cover some of the most common use cases, such as children, elements, and functional components.
How to pass a React component as a prop in TypeScript
Before looking at other methods we first need to look at one of the most common and basic ways in which you can pass a React component as a prop in TypeScript, and this simply involves using a built-in type from React called React.ReactNode
along with React Elements.
A ReactNode is simply a type that indicates that the value can belong as part of the React tree, in other words, you should be able to plug it into your JSX and it should work.
You might have seen this before with the children prop in React which passes down one of many components as a prop called children.
Here is a quick example of how that might look:
export function ExampleComponent({
children,
}: {
children: React.ReactNode | React.ReactNode[];
}) {
return (
<div>
<h1>Example of how to pass a React component as a prop in TypeScript</h1>
{children}
</div>
);
}
export default function App() {
return (
<ExampleComponent>
<h3> Hello World! </h3>
</ExampleComponent>
);
}
That small bit of code will produce an outcome like so:
As you can see the ExampleComponent worked as a wrapper for our JSX which was treated, in this instance, as a ReactElement which is accepted as a valid ReactNode.
This pattern is common and frequently used because it makes it possible to make use of JSX elements in the same way in which we use HTML elements by nesting them to create UI’s which makes it nice and simple to use.
The same approach can be used if you want to accept a component as a specific prop rather than nesting it within a JSX element.
Here is the same example of how to pass a React component as a prop in TypeScript:
export function ExampleComponent({ body }: { body: React.ReactNode }) {
return (
<div>
<h1>Example of how to pass a React component as a prop in TypeScript</h1>
{body}
</div>
);
}
export default function App() {
return <ExampleComponent body={<h3> Hello World! </h3>} />;
}
This works because anything used within curly braces in JSX will be run as JavaScript and then returned to the JSX in that position.
So by calling children or our example of body
we are saying to return that data into the JSX at that position, you can learn more about components and JSX here.
Before we move onto the next way of passing in a React component as a prop in TypeScript, it should be said it is better practice to make use of the children prop because it is a widely known pattern and creates neat looking applications when combined with JSX.
How to pass a functional React component as a prop in TypeScript
The next way to pass a React component as a prop in TypeScript is as a functional component which means instead of saving the component as a variable of ReactElements we want to be able to call the component and use it in the same way as we would have as if we imported the component.
So, how do you pass a functional React component as a prop in TypeScript?
Before we look at some examples there is a question to consider when passing down a functional React component as a prop, which is does the component accept any props?
If the answer to the above question is yes, then we need to make use of generic types in order to be able to preserve those types, if not then we won’t have to use generics (don’t worry we will cover how to use generics here).
Let’s first look at an example of passing a functional component as a prop in TypeScript that does not accept any props:
const Example = ({ SomeComponent }: { SomeComponent: React.FC }) => {
return (
<div>
<h1>Example</h1>
<SomeComponent />
</div>
);
};
export default function App() {
const ExampleFC = () => <h3>Your chosen fruit is:</h3>;
return (
<div className="App">
<Example SomeComponent={ExampleFC} />
</div>
);
}
As you can see, all we are doing here is passing the functional component as a prop which is saved in the constant variable called ExampleFC
, and then within the Example
component we have changed the type from React.ReactNode
to React.FC
instead.
With these two changes we can now make use of this component in the same way we would with any other component, until it comes to passing in props to the functional component.
In order to send props to the functional component it is time to make use of generic types(generics) within TypeScript.
Generics is a fairly straightforward concept, all it really means is that we can have the ability to pass types into other types when making use of them.
Here is our example updated to make use of generics:
interface ExampleFCProps {
fruit: string;
}
const Example = ({
SomeComponent,
}: {
SomeComponent: React.FC<ExampleFCProps>;
}) => {
return (
<div>
<h1>Example</h1>
<SomeComponent fruit="apples" />
</div>
);
};
export default function App() {
const ExampleFC = ({ fruit }: ExampleFCProps) => (
<h3>Your chosen fruit is: {fruit}</h3>
);
return (
<div className="App">
<Example SomeComponent={ExampleFC} />
</div>
);
}
Here is the result from the above changes:
Now when looking at the differences in the code you will see two key changes where we made use of generics.
The first key change was to our functional component where we have created some props and created a simple interface to show what props the functional component accepts, in our case it accepts a string that can be passed in as the name fruit
.
The second key change was to pass the props for the functional component down to the component that would end up consuming it so it will also receive the type checking for the fruit props.
This was done using generics.
In order to make this change all we needed to do was to change the React.FC
type so that it is aware of our own custom types.
To do this, you just need to add an open and closing carats (more than and less than symbols <>
) and then pass in the types of the functional component, in our case these are saved in the interface ExampleFCProps
so the final outcome for the type for that functional component in TypeScript will become React.FC<ExampleFCProps>
.
To put it in a few words, the type React.FC<ExampleFCProps>
is saying that it will accept a functional component with the props of ExampleFCProps
.
And that is how to pass a functional React component as a prop in TypeScript!
Whilst this method does work and there might be some cases where it is necessary, in general it is best to avoid passing functional components through props and instead consider if you can extract them and use them with modules to import/export them.
Summary
There we have how to pass a React component as a prop in TypeScript, if you want more like this be sure to check out some of my other posts!