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:
<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:
<MyButton icon="💩"> CLICK ME </MyButton> This component should accept the icon and children properties and its definition may looks like the following:
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:
<MyButton icon="💩"> DO SOMETHING</icon> <MyButton icon="😱"> CLICK ME</icon> 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:
<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:
// 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 to combine our custom props with all the HTMLButtonElement properties to automatically support all native buttons properties:
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:
// 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 to create a rest property that contain all the properties except them:
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:
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:
<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
🔗 Follow me on:

Top comments (0)