Open-Source Wikis

/

Tailwind CSS

/

Features

/

v3 compatibility

tailwindlabs/tailwindcss

v3 compatibility

Tailwind v4 is a near-total rewrite, but the v3 plugin and config ecosystem keeps working. The bridge that makes that possible is packages/tailwindcss/src/compat/.

What "v3 compat" means

Three things in v3 that v4 needed to keep working:

  1. JavaScript config files (tailwind.config.js, tailwind.config.ts). Loaded via @config "./tailwind.config.js" in CSS.
  2. JavaScript plugins (module.exports = require('tailwindcss/plugin')(({ addUtilities, matchUtilities, addVariant, addBase, addComponents, theme, e, prefix, config, ... }) => { … })). Loaded via @plugin "./plugin.js".
  3. Re-exports like tailwindcss/colors, tailwindcss/defaultTheme, tailwindcss/plugin, tailwindcss/lib/util/flattenColorPalette. These are still re-exported from the compat directory.

Directory layout

packages/tailwindcss/src/compat/
├── apply-compat-hooks.ts          # Orchestrator (~520 lines)
├── plugin-api.ts                  # The v3 plugin API surface (~750 lines)
├── plugin-functions.ts            # Helpers like `e`, `prefix`, `theme`, `config`
├── apply-config-to-theme.ts       # config.theme → v4 Theme
├── apply-keyframes-to-theme.ts    # config.theme.keyframes → v4 Theme
├── colors.ts / colors.cts         # Default color palette (re-exported as tailwindcss/colors)
├── default-theme.ts / .cts        # Default theme object (re-exported as tailwindcss/defaultTheme)
├── flatten-color-palette.ts/.cts  # Helper re-exported as tailwindcss/lib/util/flattenColorPalette
├── legacy-utilities.ts            # Utilities present only for v3 backwards compat
├── screens-config.ts              # config.theme.screens → breakpoint variants
├── dark-mode.ts                   # config.darkMode → Tailwind v4 dark variant
├── container.ts                   # The v3 `container` component utility
├── theme-variants.ts              # config.theme.* → CSS theme tokens
├── config/
│   └── types.ts                   # The full v3 config type
└── *.test.ts                      # Tests

How v3 configs become v4 design systems

graph LR
    UCfg[User config file] -->|loaded by jiti| LM[loadModule]
    LM --> ACH[applyCompatibilityHooks]
    ACH --> ACT[apply-config-to-theme]
    ACH --> AKT[apply-keyframes-to-theme]
    ACH --> SC[screens-config]
    ACH --> DM[dark-mode]
    ACH --> Cont[container component]
    ACH --> LU[legacy-utilities]
    ACH -->|registers utilities/variants| DS[v4 DesignSystem]

applyCompatibilityHooks is called from parseCss in packages/tailwindcss/src/index.ts after the design system is constructed but before utilities run. It runs through every @plugin and @config directive seen during parsing, executes them with the compat plugin API, and translates the resulting registrations into v4 form.

The compat plugin API

packages/tailwindcss/src/compat/plugin-api.ts implements the API v3 plugins are written against:

v3 API What it does in v4
addUtilities(obj) Registers each entry as a static utility on the v4 design system.
matchUtilities(...) Registers as a functional utility, with the values map handled the v4 way.
addComponents(obj) Registers as utilities at a different layer (so they sort below utilities).
addBase(obj) Adds CSS to the @layer base cascade.
addVariant(name, sel) Variants.static(...) registration.
matchVariant(...) Variants.functional(...) registration.
theme(path) Looks up the v4 Theme (with v3 paths translated) and returns the resolved value.
config(path) Reads from the merged v3 config object.
e(value) CSS escape — falls through to escape from packages/tailwindcss/src/utils/escape.ts.
prefix(class) Adds the configured prefix to a class name (or returns it unchanged).

PluginWithConfig is the type plugins implement. The CHANGELOG entry for v4.2.4 mentions "Export missing PluginWithConfig type from tailwindcss/plugin" (PR #19707) — exporting that type was needed for TypeScript users to type their plugin output correctly.

How @config and @plugin get loaded

Both are at-rules recognized by parseCss. They store the absolute path of the JS file in a list. After parsing, applyCompatibilityHooks walks the list and calls loadModule (provided by the adapter via @tailwindcss/node) for each. The loaded module's exports run through the compat plugin API.

The compat layer does not eagerly import any plugin files; it relies entirely on loadModule callbacks from the adapter. In @tailwindcss/browser, loadModule always throws — which is why the browser bundle does not support plugins or JS configs.

v3 quirks the compat layer preserves

  • Class prefix. v3's prefix: 'tw-' config translates to v4's @import 'tailwindcss' prefix(tw) syntax. The compat layer reads config.prefix and applies it accordingly.
  • darkMode: 'class' | 'media' | 'selector'. Translated by dark-mode.ts into the appropriate variant registration.
  • screens shape. The v3 screens config can be a record of named breakpoints, with min/max forms. screens-config.ts translates these into v4 @media variant registrations.
  • flattenColorPalette quirks. Some v3 plugins call flattenColorPalette(theme.colors) to get a flat object of { "red-500": "#…" }. The function is preserved in flatten-color-palette.ts.
  • container component. container.ts registers it manually because v3's container was a special-case component, not a regular utility.
  • legacy-utilities.ts — utilities that v3 had but v4 deprecated, kept around so compat templates still produce CSS.

What v3 plugins lose

  • addBase(obj) content is emitted into v4's base layer, but the precise cascade ordering relative to v4's own preflight may differ — plugins that relied on a specific ordering may need adjustment.
  • Plugins that mutated v4-only internals (none should, but defensive code patterns from v3 might) won't see them.
  • Anything that was already deprecated in v3 (e.g. some long-removed CSS properties) is not re-introduced.

Testing

Compat layer tests are extensive:

  • compat/config.test.ts (~1,500 lines) — config loading, merging, and theme translation.
  • compat/plugin-api.test.ts (~5,500 lines) — every helper in the plugin API.
  • compat/screens-config.test.ts, compat/container-config.test.ts, compat/legacy-utilities.test.ts, compat/apply-config-to-theme.test.ts — focused tests per concern.

Adding new v3 compat coverage usually means adding a snapshot to one of these files.

For migration off v3 entirely see @tailwindcss/upgrade.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

v3 compatibility – Tailwind CSS wiki | Factory