---
title: "Tailwind v4: Plugins and Extensions"
slug: "tailwind-plugins"
category: "frontend"
tags: ["tailwind", "css", "frontend", "plugins", "typography", "container-queries", "forms"]
status: "stable"
last_updated: 2026-05-14
summary: "Use @plugin to load first-party and custom plugins; write custom utilities as CSS functions; compose plugins without JavaScript."
related: ["[[frontend/tailwind]]", "[[frontend/css]]", "[[frontend/css-container-queries]]", "[[frontend/tailwind-theme]]", "[[frontend/tailwind-arbitrary-values]]"]
---

## Overview

Tailwind v4 replaced the JavaScript plugin API with a CSS-native `@plugin` directive. First-party plugins (typography, forms, container-queries) load with one line. Custom utilities and variants live in plain CSS. The parent reference is [[frontend/tailwind]].

## Load first-party plugins with `@plugin`

Add a first-party plugin the same way you would add any CSS import.

```css
@import "tailwindcss";

@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
@plugin "@tailwindcss/container-queries";
```

Install the package first: `npm install @tailwindcss/typography`. No JavaScript config file needed.

- `@tailwindcss/typography` adds the `prose` class for rich text bodies: `<article class="prose">`.
- `@tailwindcss/forms` resets form elements to a consistent baseline without killing customization.
- `@tailwindcss/container-queries` adds `@lg:`, `@md:`, and other container-size variants. For the underlying CSS mechanism, see [[frontend/css-container-queries]].

## Customize first-party plugins with `@theme` overrides

Typography and forms both read from your `@theme` tokens. Override them by setting the relevant custom properties after the `@plugin` line.

```css
@plugin "@tailwindcss/typography";

@theme {
  --tw-prose-body: var(--color-ink);
  --tw-prose-headings: var(--color-ink);
  --tw-prose-links: var(--color-brand-500);
  --tw-prose-code: var(--color-brand-700);
}
```

This keeps prose colors in sync with the design system instead of hard-coding a parallel set of values.

## Write custom utilities as CSS in `@layer utilities`

For utilities that no plugin covers, write them directly in the utilities layer. They become first-class Tailwind utilities: they participate in the scanner, the Prettier sort, and the variant system.

```css
@layer utilities {
  .text-balance {
    text-wrap: balance;
  }
  .scrollbar-none {
    scrollbar-width: none;
  }
  .scrollbar-none::-webkit-scrollbar {
    display: none;
  }
}
```

Use this for single-property utilities that would otherwise be arbitrary values you type repeatedly. Three or more uses of `[text-wrap:balance]` in source means it belongs here.

## Write custom variants with `@custom-variant`

Define a custom variant for any CSS selector pattern you repeat.

```css
@custom-variant hocus (&:hover, &:focus-visible);
@custom-variant rtl ([dir="rtl"] &);
@custom-variant print (@media print { & });
```

After this definition, `hocus:text-brand` applies `text-brand` on hover or keyboard focus. The `rtl:` variant flips layout for right-to-left languages. `print:hidden` hides elements in print media.

## Publish reusable plugin packages as CSS files

A v4 plugin is a plain CSS file that follows the `@layer` and `@theme` conventions. Publish it to npm; consumers load it with `@plugin`.

```css
/* my-plugin/index.css */
@layer utilities {
  .truncate-2 {
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }
}
```

```css
/* consumer */
@plugin "my-plugin";
```

No build step, no JavaScript file, no plugin function to call. The CSS file is the plugin.

## Check v4 before reaching for a v3 plugin

Several popular v3 plugins became built-in or first-class in v4:

- `@tailwindcss/line-clamp`: the `line-clamp-*` utilities are built into v4.
- `@tailwindcss/aspect-ratio`: `aspect-*` utilities are built in.
- `@tailwindcss/typography`: still separate; see above.
- Custom `theme()` calls in v3: replaced by `var(--token-name)` in v4 since tokens are CSS custom properties.

Checking the v4 changelog before installing a plugin avoids pulling in a package that does nothing.

## Avoid composing utilities with `@apply` inside plugins

`@apply` inside a plugin adds hidden coupling between the plugin and the host project's utility set. If the host project renames a token, the plugin silently breaks.

Write plugins using direct CSS declarations and `var(--token)` references. This makes the dependency on the design system explicit and auditable.

## Related

- [[frontend/tailwind]]
- [[frontend/css]]
- [[frontend/css-container-queries]]
- [[frontend/tailwind-theme]]
- [[frontend/tailwind-arbitrary-values]]
- [[frontend/tailwind-migration]]
