React “Polymorphic Components” With TypeScript

Pavel Pogosov
Better Programming
Published in
3 min readMay 14, 2022

--

Picture of a code fragment
(source: pexels.com)

In this article, we are going to learn how to implement polymorphic components in React. Also, we will strongly type our props depending on the type of element we want to render. In our example, we will create a polymorphic button.

So what is the ‘Polymorphic Component’?

In a few words, it is a component that lets us specify which React element we want to use for its root. If you’ve used some UI library, such as Material UI, you’ve already encountered a polymorphic component.

For instance, MUI has a “Typography”, that has the ability to render as any HTML element for the root node. That’s quite a powerful idea that gives us more robust and reusable components.

The result we want to achieve

We want to create a Button, that will be able to behave mainly as a normal button, anchor, and react-router link (or any other HTML tag |component, depending on your requirements). Also, we would like to have all the needed typings for it. For example, the usual button has a “type” attribute and does not have a “href”, but the anchor button has the opposite props.

Our use case

This code demonstrates to us how we can use our button.

Making our component

Let’s create our file called Button.tsx and define the base react component.

Nothing special, just basic component

So what should we do to support more than one type of HTML element for our button? We could achieve this by defining the prop “as”, which will be the type of React.ElementType and then use it to render our root element.

Now our component is polymorphic

Notice, that we have to write “Tag” capitalized, otherwise it will be treated as an usual HTML element <tag />.

Now we have a polymorphic button, but we don’t have any props typings for various “as” values. But how will we get them if they depend on dynamic “as” prop?

That is an exact situation where we should use TypeScript generics to type everything properly. So, let’s take a stab at writing some more advanced types.

Owned props type

We have created a new type ButtonOwnProps containing button base props. Also, it consumes generic E which extends React.ElementType and assigns a value to ‘as’.

Next, we should create a type called ButtonProps, which will combine our base props with generic element props.

In addition, we should exclude props from the generic component which we have already declared in our base type.

Owned props unioned with props from the generic component type

In the first line, nothing special happens. We declared a type that receives the generic E extending React.ElementType.

In the second line, we just passed it to our base props to get the right as type.

In the third line, we omitted all the properties from the generic component that we already have in our ButtonOwnProps.

Final result

Finally, we need to pass generic to our ButtonProps type and also give it a default value. So the final component will look like this:

Now our Button component is finally made

That's it for this article. Now our Button component is ready to be used.

I hope that you liked this article and I was able to give you something new. If you have any questions or suggestions, please write them in the comments.

Good luck!

--

--

Senior Frontend Dev, sharing my knowlege about frontend. 100% original content, 0% AI