This is a quick post just to share a nice way how to do it with full TypeScript support. It uses RadixUI Slot component for the polymorphism.
import React, { forwardRef } from 'react';
import { cnMerge } from 'common/cn-merge';
import { Slot } from '@radix-ui/react-slot';
const baseClasses = /*tw:*/ `inline-flex items-center justify-center gap-2 font-bold text-center rounded-md`;
const variantsLookup = {
solid: /*tw:*/ `text-white bg-primary hover:shadow-button duration-100`,
outline: /*tw:*/ `text-primary border border-blue-300 hover:bg-blue-600 hover:border-primary`,
subtle: /*tw:*/ `text-primary hover:bg-blue-600`,
};
const sizesLookup = {
base: /*tw:*/ `px-5 py-2.5`,
};
type ButtonProps = {
children: React.ReactNode;
variant?: 'outline' | 'solid' | 'subtle';
size?: 'base';
href?: string;
className?: string;
asChild?: boolean;
};
const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
const { children, variant, size, asChild, className, ...rest } = props;
const classes = cnMerge([
baseClasses,
variantsLookup[variant || 'solid'],
sizesLookup[size || 'base'],
className,
]);
const Comp = asChild ? Slot : 'button';
return (
<Comp {...rest} className={classes} ref={ref}>
{children}
</Comp>
);
});
export default Button;
Usage:
<NextLink href="/extension" passHref>
<Button asChild>
<a>
<ExtensionIcon />
Install Chrome Extension
</a>
</Button>
</NextLink>
/* ---- */
<Button asChild>
<a href="https://wwww.test.com" target="_blank" rel="noopener noreferrer">
<ExtensionIcon />
Install Chrome Extension
</a>
</Button>
Top comments (0)