Tailwind CSS - Plugins


Tailwind CSS Plugins allows to extend Tailwind with reusable third-party plugins. Plugin let you add new styles to Tailwind using javascript instead of regular CSS code.

To create your first plugin, you need to do two things:

  • Import the plugin function: This is done by adding import { plugin } from 'tailwindcss/plugin' to your Tailwind CSS configuration file.
  • Add your plugin to the plugins array: You do this by calling the plugin function within your plugins array. Inside the plugin function, you'll write the code for your plugin.
const plugin = require('tailwindcss/plugin')

module.exports = {
    plugins: [
    plugin(function({ addUtilities, addComponents, e, config }) {
        // Add your custom styles here
    }),
    ]
}

Plugin functions are designed to be flexible. They accept a single object as input, which can be broken down into multiple helper functions for easier processing.

  • 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 static variants.
  • matchVariant() for registering custom dynamic variants.
  • theme() for looking up values in the users theme configuration.
  • config() for looking up values in the users 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

Tailwind CSS has a few official plugins for features that are not yet in the core library. These plugins can be installed using npm and then added 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'),
  ]
}

Typography

The Tailwind CSS Typography plugin makes it easy to style text in your content. It gives you pre-built classes that add stylish typography to content from places like Markdown or CMS databases, making your text look good without much effort.

<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>

Forms

The @tailwindcss/forms plugin makes it easier to style forms by providing a set of default styles. This makes it simpler to create consistent looking forms.

Aspect Ratio

The @tailwindcss/aspect-ratio plugin provides a way to set aspect ratios for elements in older browsers that don't natively support this feature. It adds new utility classes like aspect-w-* and aspect-h-* to control the width and height ratio of elements.

Container queries

The "@tailwindcss/container-queries" plugin allows you to style an element based on the size of its parent container using "@container" directive.

<div class="@container">
  <div class="@lg:text-sky-400">
     <!-- ... -->
  </div>
</div>

Adding Utilities

The addUtilities and matchUtilities functions let you add custom styles to Tailwind CSS, just like the default styles. However, these custom styles will only be included in your final CSS if you actually use them in your project.

Static Utilities

The 'addUtilities' function is used to add simple, unchanging styles (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',
        },
      })
    })
  ]
}

Dynamic Utilities

The 'matchUtilities' function lets you create CSS utility classes that use values from your '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') }
        )
    })
    ]
}

You can use custom values in your utilities, even if they aren't defined in your 'theme', by using square brackets.

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

Prefix and important

The plugin's tools automatically use the user's settings for things like 'prefixes' and 'important' options.

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;
    }

Using with modifiers

Any custom utilities that you add using addUtilities can be combined with other utility classes like hover, focus, etc.

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

Providing default values

Utility plugins can set default values by providing a configuration object as the second argument to the plugin function. These defaults behave like the original default settings and can be altered or added to by the user.

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',
    }
    }
})

Adding Components

The 'addComponents' function in Tailwind CSS lets you create custom pre-designed components like buttons, forms, alerts, and so on. These components are like building blocks that you can use in your designs, and you can modify their appearance using other Tailwind classes if needed.

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'
          },
        },
      })
    })
  ]
}

Prefix and important

By default, component classes follow the user's prefix preference but ignore their "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;
    }

It is usually not a good idea to make component declarations important. However, if you absolutely need to do this, you can manually add the '!important' keyword.

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;
}

Using with modifiers

Any custom utilities that you add using addUtilities can be combined with other utility classes like hover, focus, etc.

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

Adding base styles

The addBase function in Tailwind CSS lets you add fundamental styles that apply globally to your entire project. This is ideal for setting things like default font styles, resetting browser defaults, or defining custom fonts.

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') },
        })
    })
    ]
}

Adding variants

The 'addVariant' and 'matchVariant' functions let you create your own custom modifiers that can be used just like built-in variants like hover, focus, or supports.

Static variants

Use the addVariant function to create simple custom variants. Provide the name of your custom variation and a format string that describes 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>

Dynamic variants

The matchVariant function allows you to create custom parameterized variants, similar to the built-in variants like supports-*, data-*, and aria-*.

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 supports 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);
    },
    });

Parent and sibling states

Tailwind's special features like group-* and peer-* don't work with custom modifiers automatically. To make them work, you need to register the custom modifiers as separate variants using :merge. This makes sure that the group and peer classes appear only once in the final stylesheet, ensuring correct application.

const plugin = require('tailwindcss/plugin')

module.exports = {
    // ...
    plugins: [
    plugin(function({ addVariant }) {
        addVariant('optional', '&:optional')addComponents([
  {
    '@media (min-width: 500px)': {
      // ...
    }
  },
  {
    '@media (min-width: 500px)': {
      // ...
    }
  },
  {
    '@media (min-width: 500px)': {
      // ...
    }
  },
])
        addVariant('group-optional', ':merge(.group):optional &')
        addVariant('peer-optional', ':merge(.peer):optional ~ &')
    })
    ]
}

Extending the configuration

Plugins can add their own settings to the user's 'tailwind.config.js' file 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',
    }
  }
})

Exposing options

The plugin.withOptions API allows you to configure plugins using a custom configuration object. This gives users the ability to adjust the plugin's behavior in a way that doesn't directly relate to the theme settings. For example, you can use this API to let users specify the class names used by your plugin. The API works similarly to the regular plugin API, but instead of directly passing values, you define functions that take the user's configuration options and return the corresponding values for the plugin.

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')
  ],
}

CSS-in-JS syntax

Tailwind's plugin system uses JavaScript objects to write CSS rules, similar to how CSS-in-JS libraries like Emotion do. This uses 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)',
    }
    })

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)': {
      // ...
    }
  },
])