A utility designed to ease maintenance and organization of Tailwind classes.
See tagged-classnames
in action here.
PNPM:
pnpm add tagged-classnames
NPM:
npm install tagged-classnames
Yarn:
yarn install tagged-classnames
Import tagged-classnames
and use it to make long class lists more maintainable.
import tw from "tagged-classnames";
export default function Example() {
return (
<button
type="button"
className={tw`
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600
dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800
`}
>
Button
</button>
);
}
The same can be accomplished using only a template literal, however, all whitespace would be retained. This is what that looks like in the DOM:
<button type="button" class="
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600
dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800
">Button</button>
This is what the same element looks like in the DOM after using tagged-classnames
:
<button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Button</button>
tagged-classnames
gives you the flexibility template literals offer while keeping the resulting DOM tidy.
tagged-classnames
retains the ability to use string interpolation as with plain template literals, but with an
important caveat.
import tw from "tagged-classnames";
export default function Example({ someCondition }) {
return (
<button
type="button"
className={tw`
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
${someCondition ? `font-medium` : `font-light`} rounded-lg text-sm px-5
py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none
dark:focus:ring-blue-800
`}
>
Button
</button>
);
}
Don't do this:
<button
type="button"
className={tw`
text-white bg-${someCondition ? `blue` : `green`}-700 hover:bg-blue-800
focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5
mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none
dark:focus:ring-blue-800
`}
>
Button
</button>
The above example will produce bg-
, -700
, and one of blue
or green
classes. Use whole class names like in
the first string interpolation example if you need to conditionally apply classes.
import tw from "tagged-classnames";
const buttonStyles = tw`
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600
dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800
`;
export default function Example() {
return (
<button type="button" className={buttonStyles}>Button</button>
);
}
tagged-classnames
supports line comments in the tag function's template literal to help document complex style rules.
import tw from "tagged-classnames";
export default function Example() {
return (
<button
type="button"
className={tw`
// Comment 1
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
// Comment 2
font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600
// Comment 3
dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800
text-base // Comment 4
`}
>
Button
</button>
);
}
Comments are not rendered in the DOM and don't affect the formatting of the class list. String interpolation is not allowed in comments and will throw an error.
Don't do this:
<button
type="button"
className={tw`
// This is invalid and will throw an error: ${`text-base`}
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
`}
>
Button
</button>
C-style block comments are also supported. The same string interpolation limitations as with line comments also applies to block comments.
import tw from "tagged-classnames";
export default function Example() {
return (
<button
type="button"
className={tw`
/* Block comment on one line */
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600
/**
* Block comment spanning multiple lines
*/
dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800
/* Don't do this! ${`text-normal`} An error will be thrown */
`}
>
Button
</button>
);
}
Additionally, block comments must be properly terminated or an error will be thrown.
<button
type="button"
className={tw`
/* This is invalid and will throw an error due to an absent comment terminator
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
`}
>
Button
</button>
An alternative function cx
is available for faster class list rendering if you don't need to use comments.
import { cx } from "tagged-classnames";
export default function Example() {
return (
<button
type="button"
className={cx`
text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300
font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600
dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800
`}
>
Button
</button>
);
}
If you're worried about performance, it's a good idea to use cx
by default, and only use the default function if
you need to comment your classes.
tw
(default)Removes excessive whitespace and comments from CSS class lists. Enables flexible formatting of long lists of CSS classes.
function tw(classNames: TemplateStringsArray, ...args: string[]): string {}
cx
Removes excessive whitespace from CSS class lists. Does not support comments. More performant than tw
.
function cx(classNames: TemplateStringsArray, ...args: string[]): string {}
tagged-classnames
tag function, all interpolated values will be appended
to the end of the class list.Please report any issues with this software here. If you would like to contribute to this project, feel free to fork it and send a pull request. Note that this project is governed by a code of conduct.
This project is MIT licensed.