Polymorphic component
Tags: react, javascript
Published: 2023-03-09 11:05:02

## TL;DR

Polymorphic component is a React component that has ability to use other HTML element as the returned element.

```jsx
// Example of polymorphic component
<Button as="a" href="...">
  About
</Button>
```

With this approach, it gives you a control to customize the returned element from a component.

This is one of UI component that implement polymorphic component.

{% embed https://github.com/mantinedev/mantine %}

## Introduction

When building user interface, we often facing an issue where a component not flexible enough.

Let's say you have a component like this. 

```jsx
function Button({ children, ...props }) {
  return <button {...props}>{children}</button>;
}
```

You can use component above to create a beautiful button. Nothing wrong with that component. Until someday you want create a link with same style with the `<button />` component.

Maybe you will use this approach. 

```jsx
<a href="...">
  <Button>To next page</Button>
</a>
```

Of course it should work, but the code doesn't look so fluent. Meaning to say. If you use HTML, you can write it like this:

```jsx
// We assume <a /> using same styling with <button />
<a href="...">To next page</a>
```

Based on the problem above, polymorphic component offers you a solution like this:

```jsx
<Button as="a" href="...">To next page</a>
```

See? You don't need to wrap the `<Button />` component using `<a />` because it's automatically convert the the returned element to `<a />`.

## Building The Component

Now let's build the component and it's very easy. All you need is only React `createElement` API.

> 💡**NOTE:**
> Read the [`createElement` API documentation](https://reactjs.org/docs/react-api.html#createelement) for more information.

---

We have this component from the previous section.

```jsx
function Button({ children, ...props }) {
  return <button {...props}>{children}</button>;
}
```

From this normal component, we will refactor it to polymorphic component.

```jsx
function Button({ children, as, ...props }) {
  return React.createElement(as || 'button', props, children);
}
```

Let me explain the code above.

```diff
- function Button({ children, ...props }) {
+ function Button({ children, as, ...props }) {
  return React.createElement(as || 'button', props, children);
}
```

Here we added `as` props. This props holds a value of what element will returned.

It's not a must to name it `as`. Some UI library name it `element`, `component`, or whatever you like.

```diff
function Button({ children, as, ...props }) {
- return <button {...props}>{children}</button>;
+ return React.createElement(as || 'button', props, children);
}
```

We also changed the JSX to `createElement` API to give us more control when creating the component.

If you not familiar. Actually when you use JSX, under the hood it will transform your code to `createElement`.

By using the `createElement` API, we are able to conditionally create a component. That's why when you are not passing a value into the `as` props, the default element of `<Button />` component is `<buttton />`.

And that's it... You have created a polymorphic component.

## Conclusion

Polymorphic component may sounds very fancy and difficult, but it's not.

The term of polymorphic is a general term in computer science. You can read about it [here](https://en.m.wikipedia.org/wiki/Polymorphism_(computer_science)).

One of my favorite backend framework, Laravel, is also has a feature called [polymorphic relationships](https://laravel.com/docs/10.x/eloquent-relationships#polymorphic-relationships) which use this term.

In React world, polymorphic component refers to an ability of a component that can returns different element with a same style without removing the default functionality.

It's really help when you facing a problem where you want to use other HTML with a same style.

I hope it gives you something. Thanks for reading :)

<a href="https://www.buymeacoffee.com/thexdev" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>