React + TypeScript: Implementing the Polymorphic Button Component
As developers, creating reusable and flexible components is a crucial aspect of building scalable applications. One powerful technique to achieve this is by developing polymorphic components.
In this article, we'll explore how to create a polymorphic component in React using TypeScript, providing a versatile solution for various scenarios.
Understanding Polymorphic Components
A polymorphic component is one that can adapt its rendered element based on the context or specific requirements. In React, this flexibility is achieved by leveraging TypeScript generics. Let's dive into a practical example using a Button
component.
The Code
import React, { ElementType, ComponentProps, PropsWithChildren } from 'react';
export type ButtonProps<AS extends ElementType> =
PropsWithChildren<{
as?: AS;
}> &
Omit<ComponentProps<AS>, 'as'>;
const Button = <AS extends ElementType>(props: ButtonProps<AS>) => {
const {
as: Element = 'button',
children,
...attrs
} = props;
return (
<Element {...attrs}>{children}</Element>
);
};
The above TypeScript code defines a ButtonProps
type, utilizing TypeScript generics to make the component polymorphic. Let's break it down:
AS extends ElementType
: This generic type parameter allows us to specify the element type that our button should render. It extends theElementType
from React, ensuring that only valid HTML elements or React components can be used.PropsWithChildren
: This is a utility type that ensures our component can accept children.{ as?: AS }
: Theas
prop is the key to making our component polymorphic. It allows us to specify the element type we want the button to render as. If not provided, it defaults to the standardbutton
HTML element.Omit<ComponentProps<AS>, 'as'>
: This utility type is used to exclude theas
prop from the props of the specified element type. This is a workaround to achieve the polymorphic behaviour.
Breaking down the Button
component:
The
as
prop is destructured with a default value of'button'
. This default ensures that if no specific element is provided, the button will render as a standard HTML button.The component then uses JSX to render the specified element, passing along the rest of the props (
...attrs
) and rendering thechildren
within it.
Using the Polymorphic Button
Now that we have our polymorphic Button
component, let's explore how to use it in different scenarios:
Using as a Standard Button
<Button onClick={() => alert('Clicked!')}>Click me</Button>
Using as an Anchor (<a>
) Element
<Button as="a" href="https://example.com">Visit Example</Button>
Conclusion
In this article, we've explored the creation of a polymorphic Button
component in React using TypeScript. The flexibility provided by polymorphism allows us to adapt the component to various use cases seamlessly.