Configuration and Plugin API

Purpose and Scope

Tailwind CSS v4 is CSS-first, but it still exposes a compatibility path for JavaScript configuration and plugins so existing extensions can participate in the modern compiler. This page explains that boundary: where the core tailwindcss package accepts config and plugin modules, how integrations supply module loading, and which public package exports are intended for plugin authors or migration-compatible code. Use this page when you need to understand how custom utilities, custom variants, components, legacy config files, and JavaScript plugins enter the compiler rather than how to author every utility class by hand.

Sources: packages/tailwindcss/src/index.ts, packages/tailwindcss/package.json

The important distinction is between CSS-native customization and JavaScript compatibility. Official docs encourage design-token customization with @theme, arbitrary values with square-bracket notation, and custom CSS when the framework does not already cover a requirement. The compiler source reflects that model by treating @theme, theme(...), @apply, imports, variants, utilities, and JavaScript plugin/config compatibility as explicit feature flags during parsing. JavaScript compatibility is therefore not a separate build system; it is one feature family inside the same compilation pipeline that also handles CSS directives and utility candidate generation.

Sources: packages/tailwindcss/src/index.ts

Relevant Source Files

  • packages/tailwindcss/src/index.ts — defines the public Config interface alias, imports the compatibility hook and plugin API types, declares compiler options for loading config/plugin modules, and tracks feature flags such as JsPluginCompat, AtTheme, Variants, and Utilities.
  • packages/tailwindcss/package.json — declares the published tailwindcss package exports, including ./plugin, ./defaultTheme, ./colors, ./lib/util/flattenColorPalette, CSS entry files, and the main compiler entry point.
  • package.json — documents workspace scripts used to format, lint, build, test, run integration tests, and run UI tests for changes that affect compatibility or plugin behavior.
  • packages/@tailwindcss-browser/src/index.ts — shows how the browser runtime creates a compiler and supplies loadStylesheet and loadModule callbacks, including virtual CSS asset loading and browser-specific import limits.
  • packages/@tailwindcss-cli/src/index.ts — shows the CLI command router that sends normal builds to the build command and exposes the canonicalize utility for candidate-list normalization workflows.
  • packages/@tailwindcss-node/src/index.ts — re-exports Node integration APIs such as compile, instrumentation, optimization, source maps, and module-cache behavior used by Node-based integrations.

Core Model: CSS-First Customization with Compatibility Hooks

The first-party authoring model starts in CSS. A project imports Tailwind, declares tokens with @theme, composes generated rules with directives such as @apply, and uses class candidates in templates to request utilities. In the compiler entry point, parseCss creates a Theme, records custom variants and custom utilities, substitutes imports before deeper processing, and accumulates a Features bitmask to describe which language features were encountered. That feature accounting matters because compatibility behavior is not implicit global state; it is discovered from the input stylesheet and the modules loaded while compiling it.

Sources: packages/tailwindcss/src/index.ts

Configuration compatibility is represented by export interface Config extends UserConfig {} and by the loadModule option on compile. The loader receives an import id, a base path, and a resource hint of either plugin or config, and it returns a resolved path, base, and module whose shape is either a plugin or a config object. That contract lets the core compiler remain environment-neutral: Vite, PostCSS, the CLI, the browser runtime, and Node helpers can each decide how to resolve files, cache modules, and report dependencies without baking those filesystem rules into the CSS parser.

Sources: packages/tailwindcss/src/index.ts, packages/@tailwindcss-node/src/index.ts

Public API Surface

The tailwindcss package export map is the compatibility reference for consumers. The root export points at the compiler library and also advertises index.css as the package style entry. JavaScript extension authors get tailwindcss/plugin through both ./plugin and ./plugin.js. Migration-compatible consumers can still import tailwindcss/defaultTheme, tailwindcss/colors, and tailwindcss/lib/util/flattenColorPalette, with CommonJS and ESM targets provided in both development and publish configurations. CSS entry exports include index.css, preflight.css, theme.css, and utilities.css, plus extensionless aliases such as tailwindcss/theme.

Sources: packages/tailwindcss/package.json

Public entry pointSource-visible purpose
tailwindcssMain compiler/CSS package entry, with types from packages/tailwindcss/src/index.ts.
tailwindcss/plugin and tailwindcss/plugin.jsPublic plugin helper entry for JavaScript plugins.
tailwindcss/defaultTheme and tailwindcss/defaultTheme.jsCompatibility access to the default theme object.
tailwindcss/colors and tailwindcss/colors.jsCompatibility access to color definitions.
tailwindcss/lib/util/flattenColorPaletteCompatibility utility for plugin/config code that flattens color palettes.
tailwindcss/index.css, tailwindcss/preflight.css, tailwindcss/theme.css, tailwindcss/utilities.cssCSS-layer entry files available to imports and package consumers.

The compiler-level options form the other half of the API. CompileOptions accepts base, from, polyfills, loadModule, and loadStylesheet. base gives relative resolution a starting point; from tells import substitution whether the input has a known source; polyfills controls emitted fallbacks for @property and color-mix(...); loadStylesheet resolves CSS imports; and loadModule resolves JavaScript plugins or config files. When these callbacks are not provided and a stylesheet asks for external loading, the core throws a specific error, making integration responsibilities explicit instead of silently ignoring plugin or config directives.

Sources: packages/tailwindcss/src/index.ts

Compiler Flow for Configs, Plugins, Utilities, and Variants

A compile begins by parsing CSS into an AST context rooted at the provided base path. Imports are substituted first, because imported CSS can contribute theme variables, utilities, variants, or compatibility directives. The parser then maintains state for important, a Theme instance, maps of custom variants and their dependencies, and custom utility callbacks. The feature enum shows the compiler-recognized extension points: AtApply, AtImport, JsPluginCompat, ThemeFunction, Utilities, Variants, and AtTheme. Those names are useful when debugging behavior because they map reader-facing concepts to concrete compiler phases.

Sources: packages/tailwindcss/src/index.ts

Custom variants and utilities ultimately participate in the same design-system pipeline as built-in utilities. The source imports buildDesignSystem, compileCandidates, createCssUtility, selector-compounding helpers, and substituteAtVariant, which shows that plugin-contributed behavior is normalized into compiler concepts rather than emitted as opaque text. Practically, this means extension authors should think in terms of Tailwind primitives: define tokens for reusable design values, define utilities for class candidates, define variants for conditional selectors or at-rules, and let the compiler compose them with responsive, state, and arbitrary-value behavior where the API allows it.

Sources: packages/tailwindcss/src/index.ts

A minimal CSS-first customization keeps JavaScript out of the path entirely:

@import "tailwindcss";
 
@theme {
  --font-display: "Satoshi", "sans-serif";
  --breakpoint-3xl: 120rem;
  --ease-snappy: cubic-bezier(0.2, 0, 0, 1);
}

A compatibility-oriented stylesheet may instead include directives that require module loading, such as a legacy config or plugin reference. The exact directive syntax is parsed by the core feature path represented as JsPluginCompat; the operational requirement is that the integration calling compile must provide loadModule so those JavaScript resources can be resolved. In environments that cannot load arbitrary modules, those directives should be avoided in favor of CSS-native theme variables and custom CSS.

Integration Responsibilities

The browser package demonstrates an integration with strong constraints. It collects <style type="text/tailwindcss"> blocks, injects @import "tailwindcss" when the user has not provided imports, and compiles with base: '/', loadStylesheet, and loadModule. Its stylesheet loader resolves only known Tailwind CSS assets such as tailwindcss, tailwindcss/preflight, tailwindcss/theme, and tailwindcss/utilities to virtual in-memory files. For any other import, it throws an error saying the browser build does not support that import. That makes Play CDN-style usage convenient but intentionally narrower than a filesystem build.

Sources: packages/@tailwindcss-browser/src/index.ts

Node-based integrations provide the broader environment for compatibility. The @tailwindcss/node entry point re-exports compile-related modules, instrumentation, path normalization, optimization, and source-map helpers, then installs an ESM cache resolver hook when supported by the current Node runtime. The comments describe the compatibility constraints around Bun and Node versions, including Module.registerHooks and older Module.register. That module-loading behavior is directly relevant to config and plugin development because JavaScript modules need predictable reloading and cache-busting during watch-mode builds.

Sources: packages/@tailwindcss-node/src/index.ts

The CLI is an operator-facing integration rather than a plugin API by itself. Its entry point parses tailwindcss, tailwindcss build, and tailwindcss canonicalize, delegates build handling to the build command, and prints help for invalid or help-only invocations. For plugin authors, the practical takeaway is that CLI builds still exercise the same compiler contracts as other integrations, but command-line behavior, watch handling, input/output files, and candidate canonicalization are owned by the CLI package. When testing custom plugins, run them through the same integration path your users will use.

Sources: packages/@tailwindcss-cli/src/index.ts

Reference: Contracts and Operational Checks

Use this compact checklist when evaluating whether a config or plugin path is supported. The published package must expose the entry point you are importing, the compiler caller must provide module and stylesheet loaders when CSS references external resources, and the target runtime must be able to execute the JavaScript module format involved. The root workspace scripts provide the contributor workflow: pnpm lint checks formatting and package lint tasks, pnpm build runs Turbo builds outside playgrounds, pnpm test combines Rust tests with Vitest, and pnpm test:integrations runs the integration suite.

Sources: package.json, packages/tailwindcss/package.json

ContractConcrete namesNotes
Config typeConfig extends UserConfigPublic compiler-facing compatibility type.
Plugin module typePluginAccepted as a loadModule return module when the resource hint is plugin.
Module loaderloadModule(id, base, resourceHint)Must return { path, base, module } for plugin or config.
Stylesheet loaderloadStylesheet(id, base)Must return { path, base, content }.
Polyfill flagsPolyfills.None, AtProperty, ColorMix, AllControls fallback emission for selected CSS features.
Feature flagFeatures.JsPluginCompatIndicates @plugin or @config was used.
Plugin exporttailwindcss/pluginPublic package entry for plugin helpers.
Compatibility exportscolors, defaultTheme, flattenColorPaletteSupport older plugin/config import patterns.

For next steps, start with CSS-native customization whenever possible, then reach for the plugin export when you need reusable JavaScript extension logic. If your plugin must support browser/CDN workflows, verify that it does not depend on arbitrary imports or filesystem-only config loading. If it targets Node builds, test through the Node-backed integration used by your framework, PostCSS pipeline, Vite plugin, or CLI command. Related pages: adding-custom-styles, theme, tailwindcss-package-api, node-api, browser-build, and cli-reference.