Customization
Extending Tailwind with reusable third-party plugins.
Plugins let you register new styles for Tailwind to inject into the user’s stylesheet using JavaScript instead of CSS.
To get started with your first plugin, import Tailwind’s plugin
function from tailwindcss/plugin
. Then inside your plugins
array, call the imported plugin
function with an anonymous function as the first argument.
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addUtilities, addComponents, e, config }) {
// Add your custom styles here
}),
]
}
Plugin functions receive a single object argument that can be destructured into several helper functions:
addUtilities()
, for registering new static utility stylesmatchUtilities()
, for registering new dynamic utility stylesaddComponents()
, for registering new static component stylesmatchComponents()
, for registering new dynamic component stylesaddBase()
, for registering new base stylesaddVariant()
, for registering custom static variantsmatchVariant()
, for registering custom dynamic variantstheme()
, for looking up values in the user’s theme configurationconfig()
, for looking up values in the user’s Tailwind configurationcorePlugins()
, for checking if a core plugin is enablede()
, for manually escaping strings meant to be used in class namesWe’ve developed a handful of official plugins for popular features that for one reason or another don’t belong in core yet.
Plugins can be added to your project by installing them via npm, then adding them to your tailwind.config.js
file:
/** @type {import('tailwindcss').Config} */
module.exports = {
// ...
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/container-queries'),
]
}
The @tailwindcss/typography
plugin adds a set of prose
classes that can be used to quickly add sensible typographic styles to content blocks that come from sources like markdown or a CMS database.
<article class="prose lg:prose-xl">
<h1>Garlic bread with cheese: What the science tells us</h1>
<p>
For years parents have espoused the health benefits of eating garlic bread with cheese to their
children, with the food earning such an iconic status in our culture that kids will often dress
up as warm, cheesy loaf for Halloween.
</p>
<p>
But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
springing up around the country.
</p>
<!-- ... -->
</article>
Learn more about the typography plugin →
The @tailwindcss/forms
plugin adds an opinionated form reset layer that makes it easier to style form elements with utility classes.
<!-- You can actually customize padding on a select element: -->
<select class="px-4 py-3 rounded-full">
<!-- ... -->
</select>
<!-- Or change a checkbox color using text color utilities: -->
<input type="checkbox" class="rounded text-pink-500" />
Learn more about the forms plugin →
The @tailwindcss/aspect-ratio
plugin is an alternative to native aspect-ratio
support that works in older browsers, and adds aspect-w-*
and aspect-h-*
classes that can be combined to give an element a fixed aspect ratio.
<div class="aspect-w-16 aspect-h-9">
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
Learn more about the aspect ratio plugin →
The @tailwindcss/container-queries
plugin adds new @{size}
variants like @sm
and @md
that let you style an element based on the dimensions of a parent marked with @container
instead of the viewport.
<div class="@container">
<div class="@lg:text-sky-400">
<!-- ... -->
</div>
</div>
Learn more about the container queries plugin →
The addUtilities
and matchUtilities
functions allow you to register new styles in Tailwind’s utilities
layer.
Like with the utilities Tailwind includes by default, utilities added by a plugin will only be included in the generated CSS if they are actually being used in the project.
Use the addUtilities
function to register simple static utilities that don’t support user-provided values:
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addUtilities }) {
addUtilities({
'.content-auto': {
'content-visibility': 'auto',
},
'.content-hidden': {
'content-visibility': 'hidden',
},
'.content-visible': {
'content-visibility': 'visible',
},
})
})
]
}
Learn more about how to represent your styles in JavaScript in the CSS-in-JS syntax reference.
Use the matchUtilities
function to register utilities that map to values defined in the user’s theme
configuration:
const plugin = require('tailwindcss/plugin')
module.exports = {
theme: {
tabSize: {
1: '1',
2: '2',
4: '4',
8: '8',
}
},
plugins: [
plugin(function({ matchUtilities, theme }) {
matchUtilities(
{
tab: (value) => ({
tabSize: value
}),
},
{ values: theme('tabSize') }
)
})
]
}
Utilities defined this way also support arbitrary values, which means you can use values not present in the theme using square bracket notation:
<div class="tab-[13]">
<!-- ... -->
</div>
By default, plugin utilities automatically respect the user’s prefix
and important
preferences.
That means that given this Tailwind configuration:
/** @type {import('tailwindcss').Config} */
module.exports = {
prefix: 'tw-',
important: true,
// ...
}
…the example plugin above would generate the following CSS:
.tw-content-auto {
content-visibility: auto !important;
}
.tw-content-hidden {
content-visibility: hidden !important;
}
.tw-content-visible {
content-visibility: visible !important;
}
Any custom utilities added using addUtilities
can automatically be used with modifiers:
<div class="content-auto lg:content-visible">
<!-- ... -->
</div>
Learn more in the Hover, Focus, and Other States documentation.
Utility plugins can provide default values by including a configuration object as the second argument to the plugin
function:
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function({ matchUtilities, theme }) {
matchUtilities(
{
tab: (value) => ({
tabSize: value
}),
},
{ values: theme('tabSize') }
)
}, {
theme: {
tabSize: {
1: '1',
2: '2',
4: '4',
8: '8',
}
}
})
These values behave just like the values in the default configuration, and can be overridden or extended by the end user.
The addComponents
function allows you to register new styles in Tailwind’s components
layer.
Use it to add more opinionated, complex classes like buttons, form controls, alerts, etc; the sort of pre-built components you often see in other frameworks that you might need to override with utility classes.
To add new component styles from a plugin, call addComponents
, passing in your styles using CSS-in-JS syntax:
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addComponents }) {
addComponents({
'.btn': {
padding: '.5rem 1rem',
borderRadius: '.25rem',
fontWeight: '600',
},
'.btn-blue': {
backgroundColor: '#3490dc',
color: '#fff',
'&:hover': {
backgroundColor: '#2779bd'
},
},
'.btn-red': {
backgroundColor: '#e3342f',
color: '#fff',
'&:hover': {
backgroundColor: '#cc1f1a'
},
},
})
})
]
}
Like with other component classes in Tailwind, component classes added by a plugin will only be included in the generated CSS if they are actually being used in the project.
By default, component classes automatically respect the user’s prefix
preference, but they are not affected by the user’s important
preference.
That means that given this Tailwind configuration:
/** @type {import('tailwindcss').Config} */
module.exports = {
prefix: 'tw-',
important: true,
// ...
}
…the example plugin above would generate the following CSS:
.tw-btn {
padding: .5rem 1rem;
border-radius: .25rem;
font-weight: 600;
}
.tw-btn-blue {
background-color: #3490dc;
color: #fff;
}
.tw-btn-blue:hover {
background-color: #2779bd;
}
.tw-btn-red {
background-color: #e3342f;
color: #fff;
}
.tw-btn-red:hover {
background-color: #cc1f1a;
}
Although there’s rarely a good reason to make component declarations important, if you really need to do it you can always add !important
manually:
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addComponents }) {
addComponents({
'.btn': {
padding: '.5rem 1rem !important',
borderRadius: '.25rem !important',
fontWeight: '600 !important',
},
// ...
})
})
]
}
All classes in a selector will be prefixed by default, so if you add a more complex style like:
const plugin = require('tailwindcss/plugin')
module.exports = {
prefix: 'tw-',
plugins: [
plugin(function({ addComponents }) {
const components = {
// ...
'.navbar-inverse a.nav-link': {
color: '#fff',
}
}
addComponents(components)
})
]
}
…the following CSS would be generated:
.tw-navbar-inverse a.tw-nav-link {
color: #fff;
}
Any component classes added using addComponents
can automatically be used with modifiers:
<div class="btn md:btn-lg">
<!-- ... -->
</div>
Learn more in the Hover, Focus, and Other States documentation.
The addBase
function allows you to register new styles in Tailwind’s base
layer. Use it to add things like base typography styles, opinionated global resets, or @font-face
rules.
To add new base styles from a plugin, call addBase
, passing in your styles using CSS-in-JS syntax:
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addBase, theme }) {
addBase({
'h1': { fontSize: theme('fontSize.2xl') },
'h2': { fontSize: theme('fontSize.xl') },
'h3': { fontSize: theme('fontSize.lg') },
})
})
]
}
Since base styles are meant to target bare selectors like div
or h1
, they do not respect the user’s prefix
or important
configuration.
The addVariant
and matchVariant
functions allow you to register your own custom modifiers that can be used just like built-in variants like hover
, focus
, or supports
.
Use the addVariant
function for simple custom variants, passing in the name of your custom variant, and a format string that represents how the selector should be modified.
const plugin = require('tailwindcss/plugin')
module.exports = {
// ...
plugins: [
plugin(function({ addVariant }) {
addVariant('optional', '&:optional')
addVariant('hocus', ['&:hover', '&:focus'])
addVariant('inverted-colors', '@media (inverted-colors: inverted)')
})
]
}
The first argument is the modifier name that users will use in their HTML, so the above example would make it possible to write classes like these:
<form class="flex inverted-colors:outline ...">
<input class="optional:border-gray-300 ..." />
<button class="bg-blue-500 hocus:bg-blue-600">...</button>
</form>
Use the matchVariant
function to register new parameterized variants like the built-in supports-*
, data-*
, and aria-*
variants:
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ matchVariant }) {
matchVariant(
'nth',
(value) => {
return `&:nth-child(${value})`;
},
{
values: {
1: '1',
2: '2',
3: '3',
}
}
);
})
]
}
Variants defined with matchVariant
also support arbitrary values using square bracket notation:
<div class="nth-[3n+1]:bg-blue-500 ...">
<!-- ... -->
</div>
Use the sort
option to control the source order of the generated CSS if needed to avoid precedence issues with other values that come from the same variant:
matchVariant("min", (value) => `@media (min-width: ${value})`, {
sort(a, z) {
return parseInt(a.value) - parseInt(z.value);
},
});
Your custom modifiers won’t automatically work with Tailwind’s parent and sibling state modifiers.
To support the group-*
and peer-*
versions of your own custom modifiers, register them as separate variants using the special :merge
directive to ensure the .group
and .peer
classes only appear once in the final selector.
const plugin = require('tailwindcss/plugin')
module.exports = {
// ...
plugins: [
plugin(function({ addVariant }) {
addVariant('optional', '&:optional')
addVariant('group-optional', ':merge(.group):optional &')
addVariant('peer-optional', ':merge(.peer):optional ~ &')
})
]
}
Plugins can merge their own set of configuration values into the user’s tailwind.config.js
configuration by providing an object as the second argument to the plugin
function:
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function({ matchUtilities, theme }) {
matchUtilities(
{
tab: (value) => ({
tabSize: value
}),
},
{ values: theme('tabSize') }
)
}, {
theme: {
tabSize: {
1: '1',
2: '2',
4: '4',
8: '8',
}
}
})
This can be useful for things like providing default theme
values for the classes your plugin generates.
Sometimes it makes sense for a plugin to be configurable in a way that doesn’t really belong under theme
, like perhaps you want users to be able to customize the class name your plugin uses.
For cases like this, you can use plugin.withOptions
to define a plugin that can be invoked with a configuration object. This API is similar to the regular plugin
API, except each argument should be a function that receives the user’s options
and returns the value that you would have normally passed in using the regular API:
const plugin = require('tailwindcss/plugin')
module.exports = plugin.withOptions(function (options = {}) {
return function({ addComponents }) {
const className = options.className ?? 'markdown'
addComponents({
[`.${className}`]: {
// ...
}
})
}
}, function (options) {
return {
theme: {
markdown: {
// ...
}
},
}
})
The user would invoke your plugin passing along their options when registering it in their plugins
configuration:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
// ...
},
plugins: [
require('./plugins/markdown.js')({
className: 'wysiwyg'
})
],
}
The user can also register plugins created this way normally without invoking them if they don’t need to pass in any custom options:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
// ...
},
plugins: [
require('./plugins/markdown.js')
],
}
Tailwind’s plugin system expects CSS rules written as JavaScript objects, using the same sort of syntax you might recognize from CSS-in-JS libraries like Emotion, powered by postcss-js under-the-hood.
Consider this simple CSS rule:
.card {
background-color: #fff;
border-radius: .25rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
Translating this to a CSS-in-JS object would look like this:
addComponents({
'.card': {
'background-color': '#fff',
'border-radius': '.25rem',
'box-shadow': '0 2px 4px rgba(0,0,0,0.2)',
}
})
For convenience, property names can also be written in camelCase and will be automatically translated to dash-case:
addComponents({
'.card': {
backgroundColor: '#fff',
borderRadius: '.25rem',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
}
})
Nesting is also supported (powered by postcss-nested), using the same syntax you might be familiar with from Sass or Less:
addComponents({
'.card': {
backgroundColor: '#fff',
borderRadius: '.25rem',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
'&:hover': {
boxShadow: '0 10px 15px rgba(0,0,0,0.2)',
},
'@media (min-width: 500px)': {
borderRadius: '.5rem',
}
}
})
Multiple rules can be defined in the same object:
addComponents({
'.btn': {
padding: '.5rem 1rem',
borderRadius: '.25rem',
fontWeight: '600',
},
'.btn-blue': {
backgroundColor: '#3490dc',
color: '#fff',
'&:hover': {
backgroundColor: '#2779bd'
},
},
'.btn-red': {
backgroundColor: '#e3342f',
color: '#fff',
'&:hover': {
backgroundColor: '#cc1f1a'
},
},
})
…or as an array of objects in case you need to repeat the same key:
addComponents([
{
'@media (min-width: 500px)': {
// ...
}
},
{
'@media (min-width: 500px)': {
// ...
}
},
{
'@media (min-width: 500px)': {
// ...
}
},
])