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
Paste your tailwind.config.js (or just its theme part) into the
converter.
Replace the three @tailwind directives in your main CSS file with the output.
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 key | v4 variable | Utility example |
|---|---|---|
colors.brand | --color-brand | bg-brand, text-brand |
fontFamily.display | --font-display | font-display |
fontSize.hero | --text-hero | text-hero |
borderRadius.card | --radius-card | rounded-card |
spacing.18 | --spacing-18 | p-18, gap-18 |
screens.tablet | --breakpoint-tablet | tablet:flex |
boxShadow.soft | --shadow-soft | shadow-soft |
animation.wiggle | --animate-wiggle | animate-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:
@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@utilityand@custom-variantby hand.- darkMode: 'class'
- Becomes
@custom-variant dark (&:where(.dark, .dark *));. - prefix
- Becomes an import option,
@import "tailwindcss" prefix(tw);— and classes becometw:flexinstead oftw-flex. - content
- Gone: v4 finds your sources automatically. Add
@sourceonly 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.sidebarand similar become@utilityrules.
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.