all docsMove a Tailwind theme to v4

docs / Guides

Move a Tailwind theme to v4

How tailwind.config.js maps to v4's @theme: namespaces, extend vs override, plugins and dark mode.

Tailwind v4 is configured in CSS. The theme object from tailwind.config.js becomes an @theme block of custom properties, plugins load with @plugin, and custom variants are declared with @custom-variant. This guide shows how each part maps, and what can't be converted automatically.

tip

The free Tailwind v3 → v4 converter does every mapping on this page for you, in your browser, and lists what it skipped.

The short version

1

Paste your tailwind.config.js (or just its theme part) into the converter.

2

Replace the three @tailwind directives in your main CSS file with the output.

3

Work through the notes under the output, then delete tailwind.config.js — or keep loading it with @config "./tailwind.config.js"; while you migrate.

How theme keys map

Every theme key becomes a namespace of CSS variables, and each variable creates utilities:

v3 theme keyv4 variableUtility example
colors.brand--color-brandbg-brand, text-brand
fontFamily.display--font-displayfont-display
fontSize.hero--text-herotext-hero
borderRadius.card--radius-cardrounded-card
spacing.18--spacing-18p-18, gap-18
screens.tablet--breakpoint-tablettablet:flex
boxShadow.soft--shadow-softshadow-soft
animation.wiggle--animate-wiggleanimate-wiggle

A fontSize entry with a line height, like hero: ["4rem", { lineHeight: "1" }], becomes --text-hero: 4rem; plus --text-hero--line-height: 1;. Nested colours flatten: brand.DEFAULT becomes --color-brand and brand.600 becomes --color-brand-600.

extend vs override

In v3, keys under theme.extend added to the defaults, and keys directly under theme replaced them. In v4, adding is the default. To replace a namespace, reset it first:

css
@theme {
  --breakpoint-*: initial;
  --breakpoint-tablet: 640px;
}

Plugins, dark mode and the rest

plugins
require("@tailwindcss/typography") becomes @plugin "@tailwindcss/typography";. Inline plugin functions need porting to @utility and @custom-variant by hand.
darkMode: 'class'
Becomes @custom-variant dark (&:where(.dark, .dark *));.
prefix
Becomes an import option, @import "tailwindcss" prefix(tw); — and classes become tw:flex instead of tw-flex.
content
Gone: v4 finds your sources automatically. Add @source only for paths it misses.
safelist
Plain class names become @source inline("…"). Regex patterns have no v4 equivalent.
Named keys without a namespace
zIndex.modal, gridTemplateColumns.sidebar and similar become @utility rules.

What needs a human

Anything computed at runtime can't be read from the file: colors.slate imported from tailwindcss/colors, theme() functions, spreads of the default theme. v4 ships the default palette as variables, so a reference like colors.slate usually becomes var(--color-slate-500).

For renamed utilities in your templates (shadow-sm → shadow-xs, and so on), run the official npx @tailwindcss/upgrade on the project. The converter only handles the config.

Still stuck? Write to support@xtractly.app. We're happy to help.