Before I explain polymorphic buttons or links, let me describe what led me to write this article. After finishing an article about Polymorphic Link/Button Components, I thought it would be a good idea to use it to better manage styles.
If you’re wondering, what was that article, It was this article: Polymorphic Link/Button Components in React & TypeScript.
I’m suggesting to first read it.
Let’s imagine we have 3 styles and these styles want to be used as a Link and a Button. If we want to do it, we should to create 2 components (Link and Button) and after that for each of them we should create props and class etc.
At this moment, we understood what happened and what’s going on. Right now, the question is this: How could we manage better? Let’s go.
Create a File
Firstly, We should create a component, its name is buttonOrlink.tsx.
Create component
enum ButtonOrLinkStyleModels {
"model-1",
"model-2",
"model-3",
}
type ButtonOrLinkProps = (
| (React.ButtonHTMLAttributes<HTMLButtonElement> & { as: "button" })
| (React.AnchorHTMLAttributes<HTMLAnchorElement> & { as: "a" })
) & { styleModel: keyof typeof ButtonOrLinkStyleModels };
const ButtonOrLink = (props: ButtonOrLinkProps) => {
const classNameProps = props.className ? props.className : "";
const className = `${classNameProps} ${props.styleModel}`;
return props.as === "a" ? (
<a {...props} className={className} />
) : (
<button {...props} className={className} />
);
};
export default ButtonOrLink;
If you’ve seen the article, the most of the things it same, but I just add only add 2 things:
- Enum
- Props styleModel
Let me first explain what Enum is on that component. As I said before we have 3 styles and for each of them it’s different.
We don’t know the styles, it could be something easy for us or hard, cause sometime we must use many classes on that button or link. It would be long I mean hover, focus, disabled etc.
For better manage styles and be short we go to the global styles and on there we write our classes for each of them.
Global Style
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
background-image: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
@layer components {
.default {
@apply flex px-0 py-2 items-center justify-center
gap-[10px] border-[1.5px] border-white
shadow-lg text-xs text-white;
}
.model-1 {
@apply default bg-blue-500 hover:text-blue-500
hover:bg-white hover:border-blue-500
w-[60px] h-[20px] rounded-2xl;
}
.model-2 {
@apply default bg-gray-500 hover:text-gray-500
hover:bg-white hover:border-gray-500
w-[60px] h-[25px] rounded-lg;
}
.model-3 {
@apply default bg-black hover:text-black
hover:bg-white hover:border-black
w-[70px] h-[70px] rounded-full;
}
}
In this file, what did I do ? I wrote we have a default style, and after that I said we have 3 model style (.model-1, .model-2, .model-3) and for all of them said have a default class and special style.
Tailwind.config.ts
You have to add these class in safelist in the tailwind.config.ts
import type { Config } from 'tailwindcss'
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
},
},
plugins: [],
safelist:['model-1','model-2','model-3']
}
export default config
Props styleModel
This prop styleModel handles which of the styles we want to add to that component and second prop as determine whether our component is a Link or Button
I created two components to better help you understand.
Links
import ButtonOrLink from "./buttonOrlink";
function Links() {
return (
<>
<ButtonOrLink
href="https://www.linkedin.com/in/daniel-aghababaei/"
styleModel="model-1"
as="a"
target="_blank"
>
Linkedin
</ButtonOrLink>
<ButtonOrLink
href="https://github.com/DanielDevel"
target="_blank"
styleModel="model-2"
as="a"
>
Github
</ButtonOrLink>
<ButtonOrLink
href="https://twitter.com/danieloaei"
target="_blank"
styleModel="model-3"
as="a"
>
Twitter(X)
</ButtonOrLink>
</>
);
}
export default Links;
Buttons
"use client";
import ButtonOrLink from "./buttonOrlink";
import { useRouter } from "next/navigation";
function Buttons() {
const router = useRouter();
return (
<>
<ButtonOrLink
onClick={()=> router.push("https://www.linkedin.com/in/daniel-aghababaei")}
styleModel="model-1"
as="button"
type="button"
>
Linkedin
</ButtonOrLink>
<ButtonOrLink
onClick={()=> router.push("https://github.com/danielaei")}
type="button"
styleModel="model-2"
as="button"
>
Github
</ButtonOrLink>
<ButtonOrLink
onClick={()=> router.push("https://twitter.com/danieloaei")}
type="button"
styleModel="model-3"
as="button"
>
Twitter(X)
</ButtonOrLink>
</>
);
}
export default Buttons;
At the Conclusion it will look like this and also If you have issues, You can see on my GitHub.
Top comments (0)