Multi-prop component using Tailwind CSS & clsx
Published on 16/07/2023
Last updated on 02/11/2024
Say you want to create a button with many custom properties such as variant, color, width, etc., and each one has values that make sense to you. How would you do that? Well, it may sound silly in this day & age, but I just learned how to do that using Tailwind CSS and another neat package called clsx, that we'll use to merge class strings together and also improve our code's readability.
To walk you through it, I'll use a button—similar to the ones you see throughout my website—as an example, but the model is the same for all sorts of other components.
Installing clsx
Run the following command to install clsx:
1
Creating the foundational component
Buttons can render, under the hood, either a button or a tag, depending on the use case.
Let's kick this off by creating a generic Component that can be either of those two tags.
1
Instead of explicitly passing a tag in the return call, we've used the ternary operator to control which one gets rendered. In this case, if you end up passing an href prop, it will render as a Link component (which is a "native" Next.js component). You could also just use a if you're not using Next.js. Otherwise, it will render as a button.
Additionally, we've also used the JSX spread operator {...props} so our button can receive the many other properties we'll create for it, and similarly with className in there.
Designing the base styles
Now that the foundational structure is all set, we can write the styles common to all button instances, regardless of any property. The utility classes I've used for mine add text & container-related styles. Note that I have yet to write any color and size-based classes, as I want to have props for those.
1
Composing the properties
Similar to what you'd do in Figma, we'll think about which styles we want configurable in our button through props. You could say we're designing the "component API".
For example, two super common props we'd find in most component libraries out there are variant and color. For the former, I got inspired by Joy UI's global variants and thought about solid, soft, outline, and plain. Hopefully, you can picture how they look from the names. And then, I'll use orange (which is my website's primary color), neutral, and white for the colors.
To create those, we'll put all of them inside a constant, like so:
1
Those are the specific utility classes I'm using for the buttons you see throughout this website. But feel free to go crazy with the properties and their values here. For instance, I've also added other props for controlling font-weight and width.
Passing the props to the component
With the above done, that's when we start to use the clsx package (don't forget to install it!) to merge all of those strings in the class attribute. We'll pass all of them alongside the base styles we've defined earlier and assign an actual name we want each prop to have. Additionally, we also need to pass them inside the button's curly brackets while declaring what their default values are (i.e., how the component will look if you use it without explicitly inserting any prop).
1
At this point, you should be able to add your button component somewhere and pick a variant and color like so:
1
Bonus #1: Adding icons props
To get a bit crazier, let's add props that allow you to render an icon and control its positioning inside the button by adding them inside the component markup, like so:
1
Note that we're using two new props: icon and iconSvg.
Let's add them to the list like so:
1
Now, whenever you want to have, say, a chevron icon render on the right side of the button's text, this is how you'd do it:
1
Bonus #2: Extending the link capacities
I recently added a "Jump to content" link on my website's navbar, which is a handy accessibility tool to help folks navigate through the site with a keyboard so they get to the main content faster, skipping all the navigation links.
To pull that off, I extended the Button's link capacities so that I can control whether it renders as a Next.js Link component or as a plain HTML anchor tag. Here's the code for that below:
1
Wrap up
To wrap up the article, here's the whole code for the button component. Hope you learned something useful!
Button demo
1
Full code snippet
1