How to create React UIKIT components in TypeScript that extends native HTML Elements
Tags: react, typescript, uikit, javascript
Published: 2022-07-08 21:50:24

In the front-end ecosystem, UiKits are collections of utilities and several components, such as:

* `<Card />`
* `<TabBar />`
* `<Carousel />`
* `<Row />` and `<Col />`
* `<GoogleMap />`

Each component has usually its own custom layout and could accepts several properties. For example, a `<GoogleMap />` component could accept the "coordinates" and a "zoom" value as properties:


```ts
<GoogleMap coords={coords} zoom={zoom} />
```

Sometimes we need to create components whose layout is created from scratch. In other cases their layout may be based of native elements. 
Some examples:

* `<MyButton />`: that extends `<button>` element capabilities
* `<MyImg />`: that extends `<img>` element
* and so on.

Let's imagine a `button` component that must display an icon next to the label that I can use as the code below:

```ts
<MyButton icon="💩"> CLICK ME </MyButton>
```

This component should accept the `icon` and `children` properties and its definition may looks like the following:

```ts
interface MyButtonProps {
  icon: string;
}

function MyButton(props: PropsWithChildren<MyButtonProps>) {
  const { icon, children } = props;
  return <button className="btn btn-primary">
    {icon} {children}
  </button>
}
```

So we can use the component in this way:

```ts
<MyButton icon="💩"> DO SOMETHING</icon>
<MyButton icon="😱"> CLICK ME</icon>
```

![Button Preview](https://res.cloudinary.com/my-notes-demo/image/upload/v1657295124/tutorial-content/dev.to/2022-07-react/Schermata_2022-07-08_alle_14.59.28.png)


---

In order to be more flexible, the `<MyButton />` component should also accepts all the native `button` properties. For example we may need to listen the `onClick` event or set the `disabled` attribute:


```ts
<MyButton icon="💩" onClick={() => {}} disabled />
```

So we can simply add them to the Component's Property Type and apply them as attributes of the `<button>` element:


```ts
// 1. Add the properties 
interface MyButtonProps {
  icon: string;  
  disabled: boolean;   // ➡️ native prop
  onClick: (e: React.MouseEvent) => void; // ➡️ native prop
  // ➡️ other native props
}

function MyButton(props: PropsWithChildren<MyButtonProps) {

  const { icon, children, onClick, disabled } = props;
  // 2. apply all props one by one
  return <button disabled={disabled} onClick={onClick} className="btn btn-primary">
    {icon}
    {children}
  </button>
}
```

What could I do to avoid manually passing all properties of a native button? 

**There is a trick!**

We can simply use an [intersection type](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html) to combine our custom props with all the `HTMLButtonElement` properties to automatically support all native buttons properties:

```ts
export function MyButton(
  props: PropsWithChildren<MyButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>>
) { 
 // ... component here ...
}
```

Now your component supports all buttons properties and we can apply them simply using this trick:

```ts
// apply all props as button attributes
return <button className="btn btn-primary" {...props}>
```

However `icon` and `children` are not valid button's properties so we can use [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) to create a `rest` property that contain all the properties except them:

```ts
  const { icon, children, ...rest } = props;

  // Now we apply all props except icons and children
  return <button className="btn btn-primary" {...rest}>
    {icon}
    {children}
  </button>
```

And that's all. 
Here the final source code to create a button that support `icon`, `children` and all native button properties:

```ts
import { PropsWithChildren } from 'react';

interface MyButtonProps {
  icon: string;
}
export function MyButton(
  props: PropsWithChildren<MyButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>>
) {
  const { icon, children, ...rest } = props;
  return <button className="btn btn-primary" {...rest}>
    {icon}
    {children}
  </button>
}

```

### USAGE EXAMPLE:

```ts
<MyButton 
  icon="💩" 
  type="submit"
  disabled={...} 
  onClick={...} 
  onMouseOver={...} 
> CLICK ME </MyButton>
```

---
You can also be interested to read this article:
[Create a React / TypeScript Generic component](https://dev.to/fabiobiondi/create-a-react-typescript-generic-component-2dal)

---

🔗  Follow me on:

* [YouTube](https://www.youtube.com/c/FabioBiondi)
* [LinkedIn](https://www.linkedin.com/in/fabiobiondi)
* [Twitch](https://www.twitch.tv/fabio_biondi)
* [Twitter](https://www.twitch.tv/fabio_biondi)