Customization

Plugins

Extending Tailwind with reusable third-party plugins.

Overview

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.

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. plugins: [
  4. plugin(function({ addUtilities, addComponents, e, config }) {
  5. // Add your custom styles here
  6. }),
  7. ]
  8. }

Plugin functions receive a single object argument that can be destructured into several helper functions:

  • addUtilities(), for registering new static utility styles
  • matchUtilities(), for registering new dynamic utility styles
  • addComponents(), for registering new static component styles
  • matchComponents(), for registering new dynamic component styles
  • addBase(), for registering new base styles
  • addVariant(), for registering custom variants
  • theme(), for looking up values in the user’s theme configuration
  • config(), for looking up values in the user’s Tailwind configuration
  • corePlugins(), for checking if a core plugin is enabled
  • e(), for manually escaping strings meant to be used in class names

Official plugins

We’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:

tailwind.config.js

  1. module.exports = {
  2. // ...
  3. plugins: [
  4. require('@tailwindcss/typography'),
  5. require('@tailwindcss/forms'),
  6. require('@tailwindcss/line-clamp'),
  7. require('@tailwindcss/aspect-ratio'),
  8. ]
  9. }

Typography

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.

  1. <article class="prose lg:prose-xl">
  2. <h1>Garlic bread with cheese: What the science tells us</h1>
  3. <p>
  4. For years parents have espoused the health benefits of eating garlic bread with cheese to their
  5. children, with the food earning such an iconic status in our culture that kids will often dress
  6. up as warm, cheesy loaf for Halloween.
  7. </p>
  8. <p>
  9. But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
  10. springing up around the country.
  11. </p>
  12. <!-- ... -->
  13. </article>

Learn more about the typography plugin →

Forms

The @tailwindcss/forms plugin adds an opinionated form reset layer that makes it easier to style form elements with utility classes.

  1. <!-- You can actually customize padding on a select element: -->
  2. <select class="px-4 py-3 rounded-full">
  3. <!-- ... -->
  4. </select>
  5. <!-- Or change a checkbox color using text color utilities: -->
  6. <input type="checkbox" class="rounded text-pink-500" />

Learn more about the forms plugin →

Line-clamp

The @tailwindcss/line-clamp plugin adds line-clamp-{lines} classes you can use to truncate text to a fixed number of lines.

  1. <p class="line-clamp-3 md:line-clamp-none">
  2. Et molestiae hic earum repellat aliquid est doloribus delectus. Enim illum odio porro ut omnis
  3. dolor debitis natus. Voluptas possimus deserunt sit delectus est saepe nihil. Qui voluptate
  4. possimus et quia. Eligendi voluptas voluptas dolor cum. Rerum est quos quos id ut molestiae fugit.
  5. </p>

Learn more about the line-clamp plugin →

Aspect ratio

The @tailwindcss/aspect-ratio plugin adds aspect-w-{n} and aspect-h-{n} classes that can be combined to give an element a fixed aspect ratio.

  1. <div class="aspect-w-16 aspect-h-9">
  2. <iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  3. </div>

Learn more about the aspect ratio plugin →


Adding utilities

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.

Static utilities

Use the addUtilities function to register simple static utilities that don’t support user-provided values:

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. plugins: [
  4. plugin(function({ addUtilities }) {
  5. addUtilities({
  6. '.content-auto': {
  7. 'content-visibility': 'auto',
  8. },
  9. '.content-hidden': {
  10. 'content-visibility': 'hidden',
  11. },
  12. '.content-visible': {
  13. 'content-visibility': 'visible',
  14. },
  15. })
  16. })
  17. ]
  18. }

Learn more about to represent your styles in JavaScript in the CSS-in-JS syntax reference.

Dynamic utilities

Use the matchUtilities function to register utilities that map to values defined in the user’s theme configuration:

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. theme: {
  4. tabSize: {
  5. 1: '1',
  6. 2: '2',
  7. 4: '4',
  8. 8: '8',
  9. }
  10. },
  11. plugins: [
  12. plugin(function({ matchUtilities, theme }) {
  13. matchUtilities(
  14. {
  15. tab: (value) => ({
  16. tabSize: value
  17. }),
  18. },
  19. { values: theme('tabSize') }
  20. )
  21. })
  22. ]
  23. }

Utilities defined this way also support arbitrary values, which means you can use values not present in the theme using square bracket notation:

  1. <div class="tab-[13]">
  2. <!-- ... -->
  3. </div>

Prefix and important

By default, plugin utilities automatically respect the user’s prefix and important preferences.

That means that given this Tailwind configuration:

tailwind.config.js

  1. module.exports = {
  2. prefix: 'tw-',
  3. important: true,
  4. // ...
  5. }

…the example plugin above would generate the following CSS:

  1. .tw-content-auto {
  2. content-visibility: auto !important;
  3. }
  4. .tw-content-hidden {
  5. content-visibility: hidden !important;
  6. }
  7. .tw-content-visible {
  8. content-visibility: visible !important;
  9. }

Using with modifiers

Any custom utilities added using addUtilities can automatically be used with modifiers:

  1. <div class="content-auto lg:content-visible">
  2. <!-- ... -->
  3. </div>

Learn more in the Hover, Focus, and Other States documentation.

Providing default values

Utility plugins can provide default values by including a configuration object as the second argument to the plugin function:

./plugins/tab-size.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = plugin(function({ matchUtilities, theme }) {
  3. matchUtilities(
  4. {
  5. tab: (value) => ({
  6. tabSize: value
  7. }),
  8. },
  9. { values: theme('tabSize') }
  10. )
  11. }, {
  12. theme: {
  13. tabSize: {
  14. 1: '1',
  15. 2: '2',
  16. 4: '4',
  17. 8: '8',
  18. }
  19. }
  20. })

These values behave just like the values in the default configuration, and can be overridden or extended by the end user.


Adding components

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:

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. plugins: [
  4. plugin(function({ addComponents }) {
  5. addComponents({
  6. '.btn': {
  7. padding: '.5rem 1rem',
  8. borderRadius: '.25rem',
  9. fontWeight: '600',
  10. },
  11. '.btn-blue': {
  12. backgroundColor: '#3490dc',
  13. color: '#fff',
  14. '&:hover': {
  15. backgroundColor: '#2779bd'
  16. },
  17. },
  18. '.btn-red': {
  19. backgroundColor: '#e3342f',
  20. color: '#fff',
  21. '&:hover': {
  22. backgroundColor: '#cc1f1a'
  23. },
  24. },
  25. })
  26. })
  27. ]
  28. }

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.

Prefix and important

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:

tailwind.config.js

  1. module.exports = {
  2. prefix: 'tw-',
  3. important: true,
  4. // ...
  5. }

…the example plugin above would generate the following CSS:

  1. .tw-btn {
  2. padding: .5rem 1rem;
  3. border-radius: .25rem;
  4. font-weight: 600;
  5. }
  6. .tw-btn-blue {
  7. background-color: #3490dc;
  8. color: #fff;
  9. }
  10. .tw-btn-blue:hover {
  11. background-color: #2779bd;
  12. }
  13. .tw-btn-red {
  14. background-color: #e3342f;
  15. color: #fff;
  16. }
  17. .tw-btn-red:hover {
  18. background-color: #cc1f1a;
  19. }

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:

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. plugins: [
  4. plugin(function({ addComponents }) {
  5. addComponents({
  6. '.btn': {
  7. padding: '.5rem 1rem !important',
  8. borderRadius: '.25rem !important',
  9. fontWeight: '600 !important',
  10. },
  11. // ...
  12. })
  13. })
  14. ]
  15. }

All classes in a selector will be prefixed by default, so if you add a more complex style like:

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. prefix: 'tw-',
  4. plugins: [
  5. plugin(function({ addComponents }) {
  6. const components = {
  7. // ...
  8. '.navbar-inverse a.nav-link': {
  9. color: '#fff',
  10. }
  11. }
  12. addComponents(components)
  13. })
  14. ]
  15. }

…the following CSS would be generated:

  1. .tw-navbar-inverse a.tw-nav-link {
  2. color: #fff;
  3. }

Using with modifiers

Any component classes added using addComponents can automatically be used with modifiers:

  1. <div class="btn md:btn-lg">
  2. <!-- ... -->
  3. </div>

Learn more in the Hover, Focus, and Other States documentation.


Adding base styles

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:

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. plugins: [
  4. plugin(function({ addBase, theme }) {
  5. addBase({
  6. 'h1': { fontSize: theme('fontSize.2xl') },
  7. 'h2': { fontSize: theme('fontSize.xl') },
  8. 'h3': { fontSize: theme('fontSize.lg') },
  9. })
  10. })
  11. ]
  12. }

Since base styles are meant to target bare selectors like div or h1, they do not respect the user’s prefix or important configuration.


Adding variants

The addVariant function allows you to register your own custom modifiers that can be used just like the built-in hover, focus, active, etc. variants.

To add a new variant, call the addVariant function, passing in the name of your custom variant, and a format string that represents how the selector should be modified.

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. // ...
  4. plugins: [
  5. plugin(function({ addVariant }) {
  6. addVariant('optional', '&:optional')
  7. addVariant('hocus', ['&:hover', '&:focus'])
  8. addVariant('supports-grid', '@supports (display: grid)')
  9. })
  10. ]
  11. }

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:

  1. <form class="flex supports-grid:grid ...">
  2. <input class="optional:border-gray-300 ..." />
  3. <button class="bg-blue-500 hocus:bg-blue-600">...</button>
  4. </form>

Parent and sibling states

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.

tailwind.config.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = {
  3. // ...
  4. plugins: [
  5. plugin(function({ addVariant }) {
  6. addVariant('optional', '&:optional')
  7. addVariant('group-optional', ':merge(.group):optional &')
  8. addVariant('peer-optional', ':merge(.peer):optional ~ &')
  9. })
  10. ]
  11. }

Extending the configuration

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:

./plugins/tab-size.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = plugin(function({ matchUtilities, theme }) {
  3. matchUtilities(
  4. {
  5. tab: (value) => ({
  6. tabSize: value
  7. }),
  8. },
  9. { values: theme('tabSize') }
  10. )
  11. }, {
  12. theme: {
  13. tabSize: {
  14. 1: '1',
  15. 2: '2',
  16. 4: '4',
  17. 8: '8',
  18. }
  19. }
  20. })

This can be useful for things like providing default theme values for the classes your plugin generates.


Exposing options

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:

./plugins/markdown.js

  1. const plugin = require('tailwindcss/plugin')
  2. module.exports = plugin.withOptions(function (options) {
  3. return function({ addComponents }) {
  4. const className = options.className ?? 'markdown'
  5. addComponents({
  6. [`.${className}`]: {
  7. // ...
  8. }
  9. })
  10. }
  11. }, function (options) {
  12. return {
  13. theme: {
  14. markdown: {
  15. // ...
  16. }
  17. },
  18. }
  19. })

The user would invoke your plugin passing along their options when registering it in their plugins configuration:

tailwind.config.js

  1. module.exports = {
  2. theme: {
  3. // ...
  4. },
  5. plugins: [
  6. require('./plugins/markdown.js')({
  7. className: 'wysiwyg'
  8. })
  9. ],
  10. }

The user can also register plugins created this way normally without invoking them if they don’t need to pass in any custom options:

tailwind.config.js

  1. module.exports = {
  2. theme: {
  3. // ...
  4. },
  5. plugins: [
  6. require('./plugins/markdown.js')
  7. ],
  8. }

CSS-in-JS syntax

Tailwind’s plugin system expect 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:

  1. .card {
  2. background-color: #fff;
  3. border-radius: .25rem;
  4. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  5. }

Translating this to a CSS-in-JS object would look like this:

  1. addComponents({
  2. '.card': {
  3. 'background-color': '#fff',
  4. 'border-radius': '.25rem',
  5. 'box-shadow': '0 2px 4px rgba(0,0,0,0.2)',
  6. }
  7. })

For convenience, property names can also be written in camelCase and will be automatically translated to dash-case:

  1. addComponents({
  2. '.card': {
  3. backgroundColor: '#fff',
  4. borderRadius: '.25rem',
  5. boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
  6. }
  7. })

Nesting is also supported (powered by postcss-nested), using the same syntax you might be familiar with from Sass or Less:

  1. addComponents({
  2. '.card': {
  3. backgroundColor: '#fff',
  4. borderRadius: '.25rem',
  5. boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
  6. '&:hover': {
  7. boxShadow: '0 10px 15px rgba(0,0,0,0.2)',
  8. },
  9. '@media (min-width: 500px)': {
  10. borderRadius: '.5rem',
  11. }
  12. }
  13. })

Multiple rules can be defined in the same object:

  1. addComponents({
  2. '.btn': {
  3. padding: '.5rem 1rem',
  4. borderRadius: '.25rem',
  5. fontWeight: '600',
  6. },
  7. '.btn-blue': {
  8. backgroundColor: '#3490dc',
  9. color: '#fff',
  10. '&:hover': {
  11. backgroundColor: '#2779bd'
  12. },
  13. },
  14. '.btn-red': {
  15. backgroundColor: '#e3342f',
  16. color: '#fff',
  17. '&:hover': {
  18. backgroundColor: '#cc1f1a'
  19. },
  20. },
  21. })

…or as an array of objects in case you need to repeat the same key:

  1. addComponents([
  2. {
  3. '@media (min-width: 500px)': {
  4. // ...
  5. }
  6. },
  7. {
  8. '@media (min-width: 500px)': {
  9. // ...
  10. }
  11. },
  12. {
  13. '@media (min-width: 500px)': {
  14. // ...
  15. }
  16. },
  17. ])