Skip to content

Rebuild @tailwindcss/turbopack and @tailwindcss/webpack when the input CSS changes - #20582

Merged
RobinMalfait merged 4 commits into
mainfrom
robin/research-issue-20581
Oct 16, 2026
Merged

RobinMalfait merged 4 commits into
mainfrom
robin/research-issue-20581

Conversation

@RobinMalfait

Copy link
Copy Markdown
Contributor

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 #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 (#17554), and it compares the input CSS with the input CSS of the last build since #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.

Sent by an AI assistant on behalf of @RobinMalfait.

The loader decides between an incremental and a full rebuild only by the
mtimes of the input file and its dependencies. After a save, Turbopack can
run the loader with the previous version of the CSS file first (the file
on disk already has the new mtime), and then again with the new version.
The first call rebuilt the compiler from the old source and stored the new
mtime, so the second call saw no change and reused that stale compiler.
The dev server then kept serving the CSS of the previous edit.

Store the source on the cache entry and do a full rebuild when it
differs, like `@tailwindcss/postcss` does since
#20310.

Fixes #20581
The Turbopack loader is a copy of this loader, and it has the same gap:
the rebuild check only looks at the mtimes of files on disk. When a loader
that runs before it (e.g. `sass-loader`) inlines a partial, an edit of
that partial changes the CSS that the loader receives, but not the mtime
of the input file. The loader then reused the cached compiler and emitted
the CSS of the previous build.

Do a full rebuild when the source differs from the last build, like
`@tailwindcss/postcss` does since
#20310.
The matrix of the integration tests had no `turbopack` entry, so the
tests in `integrations/turbopack` never ran in CI.
@RobinMalfait
RobinMalfait requested a review from a team as a code owner October 16, 2026 11:09
@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 544f7f0a-1af0-4c78-a087-c7084f313c59
📥 Commits

Reviewing files that changed from the base of the PR and between bb0f73a and c105b8e.

📒 Files selected for processing: 1
  • CHANGELOG.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Walkthrough

The Turbopack and webpack loaders now cache the last CSS source and select a full rebuild when the current source differs. New integration tests check CSS updates through both loaders, and the integration-test matrix now includes Turbopack.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to c105b

Both loaders now rebuild for changed CSS, with integration coverage for updated output. No concrete merge-blocking risk was identified.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: both loaders rebuild when the input CSS changes.
Description check Passed The description directly explains the stale CSS issue, the source-comparison fix, the webpack and Turbopack scope, and the added tests.
Linked Issues check Passed Issue #20581 requires the Turbopack loader to detect changed received CSS and rebuild when mtime checks are insufficient. packages/@tailwindcss-turbopack/src/index.ts stores the last source and sele…
Out of Scope Changes check Passed The webpack loader applies the same source-change cache fix to the corresponding loader. Its integration test covers CSS changes from an upstream loader. The CI matrix enables the Turbopack integratio…
Full details: Linked Issues check

Explanation

Issue #20581 requires the Turbopack loader to detect changed received CSS and rebuild when mtime checks are insufficient. packages/@tailwindcss-turbopack/src/index.ts stores the last source and selects a full rebuild when the source changes. integrations/turbopack/loader.test.ts covers repeated CSS edits during next dev and verifies that each edit is served without the previous selector.

Full details: Out of Scope Changes check

Explanation

The webpack loader applies the same source-change cache fix to the corresponding loader. Its integration test covers CSS changes from an upstream loader. The CI matrix enables the Turbopack integration target, and the changelog documents the fix. These changes support the loader fix or its regression coverage. No unrelated change is shown.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] This PR appears safe to merge.

Reviews (1) · Last reviewed commit: "Update changelog" · Reviewed by Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@tailwindcss/turbopack serves stale CSS in next dev after editing the CSS file

1 participant