React Stitches-like variants API for tailwindcss classes
npm install react-tailwind-variants[![npm package][npm-img]][npm-url]
[![npm bundle size][bundle-size-img]][bundle-size-url]
[![Downloads][downloads-img]][downloads-url]
Stitches.js-like API for creating composable components. You can define a single variant, multiple variants, and even compound variants which allow you to define classes based on a combination of variants.
This is a modified version of the classname-variants
- 📦 Lightweight
- 📜 Fully type-safe
- 💅🏼 Elegant Stitches-like variants API
- 🗑️ Automatic tailwindcss classes conflict resolution via tailwind-merge
- ♻️ Polymorphic components via @radix-ui/slot
``bash`
npm install tailwind-merge react-tailwind-variants
- Basics
- Boolean variants
- Compound variants
- Default variants
- Polymorphic components
- Composing components
- Utilities
- variants(config)
- variantProps(config)
- extractVariantsConfig(component)
- Typescript utilities
- VariantsConfigOf\
- VariantPropsOf\
- Tailwind CSS IntelliSense
Let's assume we want to build a button component with Tailwind CSS that comes in different sizes and colors.
It consists of some _base classes_ that are always present as well as some optional classes that need to be added depending on the desired _variants_.
`tsx
import { styled } from 'react-tailwind-variants';
const Button = styled('button', {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
`
The result is a react component:
`tsx`
Component will be rendered as:
`html`
type="submit"
className="rounded text-white bg-sky-500 py-4 text-base px-8"
>
Click me!
As a value for classes, you can use a "string", an "array" of strings, or "null":
`tsx
import { styled } from 'react-tailwind-variants';
const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
`
---
Variants can be of type boolean by using "true" or/and "false" as the key:
`tsx
import { styled } from 'react-tailwind-variants';
const Button = styled('button', {
base: 'text-white',
variants: {
rounded: {
true: 'rounded-full',
},
},
});
`
---
The compoundVariants option can be used to apply class names based on a combination of other variants.
`tsx
import { styled } from 'react-tailwind-variants';
const Button = styled('button', {
base: 'text-base'
variants: {
variant: {
none: null,
filled: 'bg-blue-500 text-white',
outlined: 'border border-blue-500 text-blue-500',
plain: 'bg-transparent text-blue-500',
},
size: {
sm: 'px-3 py-1.5'
md: 'px-4 py-2'
lg: 'px-6 py-3'
},
},
compoundVariants: [
{
variants: {
variant: ['filled', 'outlined'],
size: 'sm'
},
className: 'text-sm'
},
{
// compoundVariants className takesvariants
// precedence over ,p-0
// so in this case the class padding
// will override classes`
variants: {
variant: 'none'
},
className: 'p-0'
},
],
});
`tsx`
will be rendered as:
`html`
class="text-base border border-blue-500 text-blue-500 px-3 py-1.5 text-sm"
>
Outlined button
---
The defaultVariants option can be used to select a variant by default.
All non-boolean variants for which no default values are specified are required.
If no default value is specified for boolean options, it evaluates to "false"
Below is an example with a component that has a required size and an optional color variants
`tsx
import { styled } from 'react-tailwind-variants';
const Button = styled('button', {
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
elevated: {
true: 'shadow',
},
},
defaultVariants: {
color: 'neutral',
},
});
`
---
If you want to keep all the variants you have defined for a component but want to render a different HTML tag or a different custom component, you can use the "asChild" prop to do so:
`tsx
import { styled } from 'react-tailwind-variants';
const Button = styled('button', {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
`
`tsx`
will be rendered as:
`html`
href="/test"
className="rounded text-white bg-sky-500 px-6 py-4 text-base mt-4 mb-2"
>
Button as link
---
Composing one styled component into another.
1. Components can be composed via the styled function.
`tsx
import { styled } from 'react-tailwind-variants';
const BaseButton = styled('button', {
base: 'text-center bg-blue-500 text-white',
variants: {
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
const Button = styled(BaseButton, {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
`
`tsx`
will be rendered as:
`html`
type="submit"
className="text-center text-white px-6 py-4 text-base rounded text-white bg-sky-500"
>
Click me!
2. You can also achieve the same result using "asChild" prop:
`tsx
import { styled } from 'react-tailwind-variants';
const BaseButton = styled('button', {
base: 'text-center bg-blue-500 text-white',
variants: {
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
const Button = styled('button', {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
`
`tsx`
will be rendered as:
`html`
type="submit"
className="text-center text-white px-6 py-4 text-base rounded text-white bg-sky-500"
>
Click me!
---
#### variants(config)
The function accepts variants config as argument and returns a className builder function
`ts
import { variants } from 'react-tailwind-variants';
const buttonVariants = variants({
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
console.log(
buttonVariants({
color: 'brand',
size: 'small',
className: 'text-sky-900 px-8',
})
);
// Console output:
// 'rounded bg-sky-500 py-3 text-xs text-sky-900 px-8'
`
#### variantProps(config)
The function accepts variants config as argument and returns props builder function
`ts
import { variantProps } from 'react-tailwind-variants';
const buttonVariantProps = variantProps({
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
console.log(
buttonVariantProps({
color: 'brand',
size: 'small',
className: 'text-sky-900 px-8',
type: 'button',
onClick: e => {
// ...
},
})
);
// Console output:
// {
// className: 'rounded bg-sky-500 py-3 text-xs text-sky-900 px-8'
// type: "button",
// onClick: ...
// }
`
#### extractVariantsConfig(component)
The function accepts a component from which it extracts the configuration of variants
`ts
import { styled, extractVariantsConfig } from 'react-tailwind-variants';
const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
console.log(extractVariantsConfig(Button));
// Console output:
// {
// base: ['rounded', 'text-white'],
// variants: {
// color: {
// none: null,
// brand: 'bg-sky-500',
// accent: 'bg-teal-500',
// },
// },
// }
`
#### VariantsConfigOf
A utility that allows you to extract the configuration type from the component type
`ts
import { type VariantsConfigOf, styled } from 'react-tailwind-variants';
const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
type ButtonVariantsConfig = VariantsConfigOf
`
#### VariantPropsOf
A utility that allows you to extract the variant props type from the component type
`ts
import { type VariantPropsOf, styled } from 'react-tailwind-variants';
const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
type ButtonVariantProps = VariantPropsOf
`
In order to get auto-completion for the CSS classes themselves, you can use the Tailwind CSS IntelliSense plugin for VS Code. In order to make it recognize the strings inside your variants-config, you have to somehow mark them and configure the plugin accordingly.
One way of doing so is by using tagged template literals:
`ts
import { styled, tw } from 'react-tailwind-variants';
const Button = styled('button', {
base: twpx-5 py-2 text-white,bg-slate-500 hover:bg-slate-400
variants: {
color: {
neutral: tw,bg-teal-500 hover:bg-teal-400
accent: tw,`
},
},
});
You can then add the following line to your settings.json:
`(\\
"tailwindCSS.experimental.classRegex": ["tw|[^]+?)"]`
> Note
> The tw helper function is just an alias for String.raw`.
[npm-img]: https://img.shields.io/npm/v/react-tailwind-variants
[npm-url]: https://www.npmjs.com/package/react-tailwind-variants
[bundle-size-img]: https://img.shields.io/bundlephobia/minzip/react-tailwind-variants
[bundle-size-url]: https://bundlephobia.com/package/react-tailwind-variants
[downloads-img]: https://img.shields.io/npm/dt/react-tailwind-variants
[downloads-url]: https://www.npmtrends.com/react-tailwind-variants