Simple Trick to Clean Up Tailwind CSS in React
Problem
A pain point with using utility-first CSS framework like Tailwind CSS is that you inevitably end up with components such as these.
<a
href="#"
class="text-primaryBold bg-neutralBg hover:text-primary focus:border-primaryBgSofter focus:shadow-outline-indigo flex w-full
items-center justify-center rounded-md border border-transparent px-8
py-3 text-base font-medium
leading-6 transition duration-150
ease-in-out focus:outline-none md:py-4 md:px-10 md:text-lg"
>
Live demo
</a>
Consider the following:
What are the ways we would write Tailwind CSS utility classes?
It could be a string.
function Button() {
return (
<button className="... bg-blue-500 px-4 py-2 shadow-md hover:bg-blue-600">
Click
</button>
);
}
To include JavaScript, we could write a template literal.
function Button({color}) {
return (
<button
className={`${whateverJavaScript(color)}
... bg-blue-500
px-4 py-2
shadow-md
hover:bg-blue-600
`}
>
Click
</button>
);
}
The problem with template literals is in the name itself.
They are literal.
Formatters and linters such as Prettier and ESLint make no attempt to format the utility classes written in template literals, leading to files with sloppy indentation.
Solution
The solution is inspired by code in tailwindlabs/tailwindui-react.
The solution is a function.
JavaScript:
export function tw(...classes) {
return classes.filter(Boolean).join(' ');
}
TypeScript:
export function tw(...classes: (false | null | undefined | string)[]): string {
return classes.filter(Boolean).join(' ');
}
We could optionally name this function
classNames
as the original author Robin Malfait did in tailwindui-react.
I chose tw
to give brevity for the writer and context for the reader.
Now we can cleanly span our classes over many lines.
Moreover, now the formatter can easily format our files.
import Logo from '@assets/logo.svg';
import tw from '@utils/tailwind';
function Hero() {
return (
<Logo
className={tw(
'relative',
'w-full h-auto',
'mt-2 mb-4',
'text-blue-600 fill-current',
)}
/>
);
}
Group together similar classes, such as
- position
- spacing
- sizing
- color
When in doubt, group things the way Tailwind groups things on their website.
import Logo from '@assets/logo.svg';
import tw from '@utils/tailwind';
function Hero() {
return (
<Logo
className={tw(
'absolute top-2 left-2', // position
'mt-2 mb-4', // spacing
'w-full h-auto', // sizing
'text-blue-600 fill-current', // color
)}
/>
);
}
Easily include JavaScript sources.
import Logo from '@assets/logo.svg';
import tw from '@utils/tailwind';
import globalStyles from '@styles';
function Hero() {
return (
<Logo
className={tw(
'absolute top-2 left-2', // position
'mt-2 mb-4', // spacing
'w-full h-auto', // size
'text-blue-600 fill-current', // color
globalStyles.transitions,
)}
/>
);
}
In addition, we could use a plugin like Headwind to also alphabetize the classes.
I don't care that much to have my classes alphabetized, and I've found it to make for a worse DX.
Hope this helps you clean up Tailwind CSS in your own React projects.