Files
Robin Malfait f7f47f8e98 Rebuild @tailwindcss/turbopack and @tailwindcss/webpack when the input CSS changes (#20582)
This PR fixes an issue where `next dev` with the
`@tailwindcss/turbopack` loader keeps serving the CSS of the previous
edit after you edit the CSS file. Only a restart of the dev server shows
the latest version.

Fixes https://github.com/tailwindlabs/tailwindcss/issues/20581

The loader caches a compiler for each CSS file. It only does a full
rebuild when the mtime of the input file (or of one of its dependencies)
changes. After a save, Turbopack can run the loader two times:

1. With the previous version of the CSS file. The file on disk already
has the new mtime, so we did a full rebuild from the old source, and
stored the new mtime.
2. With the new version of the CSS file. The mtime did not change since
the first call, so we did an incremental build with the stale compiler
of the first call.

The loader registers the folders that it scans as context dependencies,
and the CSS file is in one of them (e.g. `app/`). So one save
invalidates the loader in two ways, and the first run can still get the
previous content of the file.

`@tailwindcss/postcss` had the same problem with Turbopack
(https://github.com/tailwindlabs/tailwindcss/pull/17554), and it
compares the input CSS with the input CSS of the last build since
https://github.com/tailwindlabs/tailwindcss/pull/20310. The loader now
does the same: it stores the source on the cache entry, and does a full
rebuild when the source changes.

`@tailwindcss/turbopack` is a copy of `@tailwindcss/webpack`, so the
webpack loader gets the same fix. With webpack, the problem shows up
when a loader that runs before ours changes the CSS, but not the input
file. For example, `sass-loader` inlines a partial, and you edit that
partial.

The matrix of the integration tests in CI had no `turbopack` entry, so
the tests in `integrations/turbopack` never ran in CI. This PR adds it.

## Test plan

- Added an integration test to `integrations/turbopack/loader.test.ts`.
It edits `app/globals.css` three times during `next dev`, and checks
that the served CSS has each edit. On `main` (with Next.js 16.4.1), the
served CSS still has `.custom-1` after the first edit. With this change,
it passes.
- Added an integration test to `integrations/webpack/loader.test.ts`,
with a loader before `@tailwindcss/webpack` that inlines
`src/partial.css`. In watch mode, it edits that partial. On `main`, the
output keeps `.partial-1`. With this change, it passes.
- Called the built loaders directly, like Turbopack does: first with the
previous source after the file changed on disk, then with the new
source. On `main`, the second call returns the CSS of the previous
version. With this change, each call returns the CSS of the source that
it gets.
- `pnpm run build` passes, and all tests in
`integrations/turbopack/loader.test.ts` and
`integrations/webpack/loader.test.ts` pass.

<sub>Sent by an AI assistant on behalf of @RobinMalfait.</sub>
2026-10-16 13:28:05 +02:00
..
2026-07-16 07:21:50 -04:00
2026-04-24 21:21:12 +02:00

Tailwind CSS

A utility-first CSS framework for rapidly building custom user interfaces.

Build Status Total Downloads Latest Release License


Documentation

For full documentation, visit tailwindcss.com.

Community

For help, discussion about best practices, or feature ideas:

Discuss Tailwind CSS on GitHub

Contributing

If you're interested in contributing to Tailwind CSS, please read our contributing docs before submitting a pull request.


@tailwindcss/webpack

A webpack loader for Tailwind CSS v4.

Installation

npm install @tailwindcss/webpack

Usage

// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack'],
      },
    ],
  },
}

Then create a CSS file that imports Tailwind:

/* src/index.css */
@import 'tailwindcss';

Options

base

The base directory to scan for class candidates. Defaults to the current working directory.

{
  loader: '@tailwindcss/webpack',
  options: {
    base: process.cwd(),
  },
}

optimize

Whether to optimize and minify the output CSS. Defaults to true in production mode.

{
  loader: '@tailwindcss/webpack',
  options: {
    optimize: true, // or { minify: true }
  },
}