I wrote about Why We Use Tailwind at ConvertKit. This is about how we use Tailwind in our components.
The short version is that we use the tailwind classes directly in our components:
const Button = (props) =>
<button {...props} className="px-4 py-2 bg-gray-700 text-white" />
But, we also want our components to do a little more. We want to work with a fixed set of colors, sizes, etc. to make development easier and our UI more consistent. That way, our developers can write code like this:
<Button color="red" size="lg">Save Form</Button>
instead of having to repeat classes all the time:
<Button className="bg-red text-white px-4 py-2"/>
Here's how we do that:
const DEFAULT = "border border-solid border-transparent";
const COLORS = {
green: "text-white bg-green-400 hover:bg-green-500",
red: "text-white bg-red-400 hover:bg-red-500"
};
const Button = ({ color, children, className, ...props }) => {
className = [className, DEFAULT, COLORS[color]].join(" ");
return (
<button {...props} className={className}>
{children}
</button>
);
};
This is a simplified example of our actual component that's only handling the different colors. We have a default set of classes that all buttons have (DEFAULT
), then, we have an object representing the different colors (COLORS
). When someone passes red
as the color
prop, we use the classes that match that key: COLORS["red"]
. We merge all this together with the className
prop that that you can also pass to our component to add additional classes if necessary.
// A red button with margin at the bottom
<Button color="red" className="mb-2"/>
We follow the same pattern for sizes, variants, and different states of our components.
How are you using Tailwind in React components?
P.S. We're hiring a few full stack engineers at ConvertKit. Learn more here convertk.it/engineer
Top comments (12)
Instead of doing this join, you could consider the classnames package on npm.
className = [className, DEFAULT, COLORS[color]].join(" ");
Also, I think you shouldn't reassign prop variables like this. Other than that: nice article!
Good point! We actually do use the classnames package 🙂. I left it out here to keep the example as simple as possible. Thanks for reading!
Hi Brendan, very nice approach this!
Do you have any ideas on how to achieve the same thing in a Vue component?
Thanks! I've been meaning to check out Vue, but I've actually never used it. I tried scanning the docs a bit and found this: vuejs.org/v2/guide/class-and-style.... I think the function form a couple paragraphs down could be helpful, but I'm not sure exactly what it would look like.
I got it to work! But could you explain: why is this approach more beneficial than for example Extracting CSS components with @apply (as explained here tailwindcss.com/docs/extracting-co...)?
I think you need to take in to account some of the differences between Vue and React when answering this question.
A Vue template gives you the ability to have your code and styles live in the same .vue file. The solution presented in this article is one of many ways to achieve the same goal in React.
I also believe the apply method doesn’t allow classes like “sm:p-0” because of the colon, so you’d have to be a little bit more verbose and do a media query in which you do the apply.
Correct me if I’m wrong on that last one though.
@Daaf RE: I got it to work!
Please can you share a snippet of how you got this to work in Vue, could you make it work as a light-weight functional component ?
Thanks
Hey Charles, I indeed got it to work.
But later I realised it's no more different than extracting components using Tailwind CSS's @apply directive. In case you really want to know, I've made a small example here.
Let me know if this is working for you!
I do it very similar. Instead of colors I call them Intents. Instead of green and red I use success and error
I literally did this exact same thing over the weekend. Tailwind is the best!
How do you handle changing classes depending on the component States?
It's a nice approach, thanks. I'll use on my project as well.