Skip to content

React: Keep the act environment set while a story tears down - #36608

Merged
valentinpalkovic merged 1 commit into
storybookjs:nextfrom
malcolmwhite-surge:fix/react-teardown-act-environment
Oct 13, 2026
Merged

valentinpalkovic merged 1 commit into
storybookjs:nextfrom
malcolmwhite-surge:fix/react-teardown-act-environment

Conversation

@malcolmwhite-surge

@malcolmwhite-surge malcolmwhite-surge commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

What I did

A story whose React.lazy chunk is still loading when the story is torn down can make the next story fail with:

The current testing environment is not configured to support act(...)

The teardown returned by renderToCanvas calls await act(() => { unmountElement(canvasElement); }) with a sync callback. For a sync callback, withGlobalActEnvironment restores IS_REACT_ACT_ENVIRONMENT before it returns React's thenable. When the teardown awaits that thenable, React reopens its act queue and keeps flushing it while work remains. A lazy chunk that resolves in that window makes React call isConcurrentActEnvironment(), which sees the act queue open and the flag false, so it logs the error. Portable stories run the saved teardown at the start of the next story, so the error lands in the next story's console checks.

This PR makes the teardown callback async: await act(async () => { unmountElement(canvasElement); }). That uses the existing async branch of withGlobalActEnvironment, which keeps the flag set until React's thenable settles. The render path in the same file already passes an async callback. The teardown was the only awaited sync act in the renderer.

Related: #34708 and #34999 cover a different warning (act(async) without await). With only #34999 applied, the new test still fails. #30356 shows the same message in docs mode, with a different cause, and #31483 fixed it.

Seen with Storybook 10.6.0 and React 19.3.0. The new test fails without the fix on React 18.3.1 and 19.2.8, and passes with it.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

  1. Run yarn nx run-many -t compile, so that every Vitest project config can load.
  2. Run yarn vitest run --project @storybook/react code/renderers/react/src/renderToCanvas.test.tsx. All tests pass.
  3. In code/renderers/react/src/renderToCanvas.tsx, change the teardown back to await act(() => {. Run the same command. The test "keeps the act environment enabled while a lazy chunk resolves during teardown" fails, because "The current testing environment is not configured to support act(...)" was logged.

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Declare whether manual QA will be needed for this PR during the next release, through qa:needed or qa:skip

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

AI assistance: I prepared this change with Claude Code, then reviewed the fix and the test myself.

The teardown awaited a sync act callback. withGlobalActEnvironment restores
IS_REACT_ACT_ENVIRONMENT before it returns React's thenable, and awaiting
that thenable reopens React's act queue. A lazy chunk that resolved then
logged "The current testing environment is not configured to support
act(...)" in the next story. An async callback keeps the flag set until
React finishes.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
Fails
🚫

PR is not labeled with one of: ["qa:needed","qa:skip","qa:success"]

Generated by 🚫 dangerJS against a4849b6

@coderabbitai

coderabbitai Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (1)
.cursor/rules/spy-mocking.mdc — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 17edc413-8bdd-46bc-9202-e283c0bcd65b
📥 Commits

Reviewing files that changed from the base of the PR and between 01bdd15 and a4849b6.

📒 Files selected for processing (2)
  • code/renderers/react/src/renderToCanvas.test.tsx
  • code/renderers/react/src/renderToCanvas.tsx

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


Walkthrough

The React renderer now awaits act during unmount. A test covers a lazy component resolving while teardown is in progress and checks for an unsupported act environment error.

Changes

React renderer teardown

Layer / File(s) Summary
Awaited unmount and teardown test
code/renderers/react/src/renderToCanvas.tsx, code/renderers/react/src/renderToCanvas.test.tsx
The unmount callback now awaits act. The test resolves a suspended lazy component during teardown and checks that no unsupported act environment error is logged.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to a4849

Teardown now waits for React’s async work, and the regression test covers the lazy-resolution warning. No actionable merge-blocking risk remains.

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · 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.

@storybook-contribution-review storybook-contribution-review Bot added bug react review:3-verification Stage 2 passed: awaiting stage 3 proof-of-work verification sev:S3 Medium priority. Fix within months if possible. labels Oct 9, 2026
@storybook-contribution-review

Copy link
Copy Markdown

🤖 I am the Storybook contribution review bot; a human maintainer approved this message before it was posted.
Review phase: Runtime verification (stage 3 of 3)

We are trying a three-stage automated review: architecture, code quality, and runtime verification. This message states which stages ran and which were skipped. A human maintainer approves each public message and makes the final decision. Please tell us whether the feedback and evidence are useful.

Thank you for this fix, @malcolmwhite-surge! All three review stages passed on commit a4849b6.

  • Architecture (stage 1): passed. The fix is in the right place and is the smallest correct change. Teardown now uses the same async act callback that the render path already uses, so the existing environment handling in act-compat.ts applies to both.
  • Code quality (stage 2): passed. No blocking findings.
  • Runtime verification (stage 3): passed. We ran your new test against the base version of renderToCanvas.tsx. It failed in 5 of 5 runs on React 18.3.1 and also failed on React 19.3.0. With your change, it passed in 5 of 5 runs on React 18.3.1 and passed on React 19.3.0. On the base code, the act warning appeared whenever a lazy chunk resolved while teardown was still running, not only at the single microtask your test uses. On your branch it did not appear. The full React renderer unit suite passed on your branch with React 18 (694 passed, 0 failed).

Two optional suggestions. Neither is required:

  1. The test only checks that a warning does not appear. If a future React version moves the timing by one microtask, the test could pass without exercising the race. A positive check would make it fail loudly instead, for example asserting that the act environment flag is still true just before chunk.resolve(...) with getReactActEnvironment() from act-compat.ts.
  2. The test calls teardown() itself, and the outer afterEach then calls it a second time. This is harmless, but adding teardown = undefined; after await tearingDown; avoids the double call.

Not covered: we did not reproduce the issue in a real browser with a code-split chunk. We did not run type checking, lint, or the full renderer suite on React 19, and we ran the tests on Node 24 instead of the pinned Node 22. A maintainer makes the final decision on merging.

Thanks again for the clear test and the careful fix!

@storybook-contribution-review storybook-contribution-review Bot added review:verified Three-step review passed: architecture, code quality, runtime verification and removed review:3-verification Stage 2 passed: awaiting stage 3 proof-of-work verification labels Oct 10, 2026
@valentinpalkovic valentinpalkovic added the ci:normal Run our default set of CI jobs (choose this for most PRs). label Oct 12, 2026
@storybook-app-bot

Copy link
Copy Markdown
Contributor

Package Benchmarks

Commit: a4849b6, ran on 12 October 2026 at 11:41:00 UTC

The following packages have significant changes to their size or dependencies:

@storybook/builder-vite

Before After Difference
Dependency count 1 1 0
Self size 206 KB 195 KB 🎉 -11 KB 🎉
Dependency size 28 KB 28 KB 0 B
Bundle Size Analyzer Link Link

@storybook/builder-webpack5

Before After Difference
Dependency count 205 181 🎉 -24 🎉
Self size 92 KB 92 KB 🚨 +240 B 🚨
Dependency size 46.11 MB 38.84 MB 🎉 -7.27 MB 🎉
Bundle Size Analyzer Link Link

storybook

Before After Difference
Dependency count 73 73 0
Self size 21.57 MB 21.75 MB 🚨 +178 KB 🚨
Dependency size 31.24 MB 31.24 MB 0 B
Bundle Size Analyzer Link Link

@storybook/angular

Before After Difference
Dependency count 213 186 🎉 -27 🎉
Self size 240 KB 240 KB 🚨 +51 B 🚨
Dependency size 39.41 MB 31.68 MB 🎉 -7.72 MB 🎉
Bundle Size Analyzer Link Link

@storybook/angular-vite

Before After Difference
Dependency count 20 20 0
Self size 23.42 MB 23.42 MB 🚨 +1 KB 🚨
Dependency size 11.56 MB 11.55 MB 🎉 -11 KB 🎉
Bundle Size Analyzer Link Link

@storybook/ember

Before After Difference
Dependency count 204 180 🎉 -24 🎉
Self size 13 KB 13 KB 🚨 +18 B 🚨
Dependency size 41.43 MB 34.16 MB 🎉 -7.27 MB 🎉
Bundle Size Analyzer Link Link

@storybook/html-vite

Before After Difference
Dependency count 4 4 0
Self size 22 KB 22 KB 0 B
Dependency size 269 KB 259 KB 🎉 -11 KB 🎉
Bundle Size Analyzer Link Link

@storybook/nextjs

Before After Difference
Dependency count 469 445 🎉 -24 🎉
Self size 641 KB 641 KB 🎉 -117 B 🎉
Dependency size 69.83 MB 62.56 MB 🎉 -7.27 MB 🎉
Bundle Size Analyzer Link Link

@storybook/nextjs-vite

Before After Difference
Dependency count 100 100 0
Self size 1.42 MB 1.42 MB 🚨 +3 B 🚨
Dependency size 24.16 MB 24.14 MB 🎉 -13 KB 🎉
Bundle Size Analyzer Link Link

@storybook/preact-vite

Before After Difference
Dependency count 4 4 0
Self size 12 KB 12 KB 0 B
Dependency size 287 KB 276 KB 🎉 -10 KB 🎉
Bundle Size Analyzer Link Link

@storybook/react-native-web-vite

Before After Difference
Dependency count 117 120 🚨 +3 🚨
Self size 104 KB 29 KB 🎉 -75 KB 🎉
Dependency size 26.10 MB 26.28 MB 🚨 +185 KB 🚨
Bundle Size Analyzer Link Link

@storybook/react-vite

Before After Difference
Dependency count 78 78 0
Self size 33 KB 32 KB 🎉 -609 B 🎉
Dependency size 21.27 MB 21.26 MB 🎉 -12 KB 🎉
Bundle Size Analyzer Link Link

@storybook/react-webpack5

Before After Difference
Dependency count 289 265 🎉 -24 🎉
Self size 23 KB 23 KB 0 B
Dependency size 58.56 MB 51.29 MB 🎉 -7.27 MB 🎉
Bundle Size Analyzer Link Link

@storybook/server-webpack5

Before After Difference
Dependency count 217 193 🎉 -24 🎉
Self size 15 KB 15 KB 🎉 -12 B 🎉
Dependency size 47.39 MB 40.12 MB 🎉 -7.27 MB 🎉
Bundle Size Analyzer Link Link

@storybook/tanstack-react

Before After Difference
Dependency count 79 79 0
Self size 124 KB 124 KB 🎉 -84 B 🎉
Dependency size 21.31 MB 21.30 MB 🎉 -13 KB 🎉
Bundle Size Analyzer Link Link

@storybook/vue3-vite

Before After Difference
Dependency count 95 95 0
Self size 32 KB 32 KB 🚨 +140 B 🚨
Dependency size 18.77 MB 18.73 MB 🎉 -31 KB 🎉
Bundle Size Analyzer Link Link

@storybook/web-components-vite

Before After Difference
Dependency count 6 6 0
Self size 19 KB 19 KB 0 B
Dependency size 515 KB 423 KB 🎉 -91 KB 🎉
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 179 179 0
Self size 872 KB 863 KB 🎉 -9 KB 🎉
Dependency size 87.01 MB 87.19 MB 🚨 +177 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 172 172 0
Self size 44 KB 44 KB 0 B
Dependency size 85.50 MB 85.68 MB 🚨 +178 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 74 74 0
Self size 1.07 MB 1.07 MB 🎉 -633 B 🎉
Dependency size 52.81 MB 52.99 MB 🚨 +178 KB 🚨
Bundle Size Analyzer node node

@storybook/vue3

Before After Difference
Dependency count 91 91 0
Self size 229 KB 208 KB 🎉 -20 KB 🎉
Dependency size 18.21 MB 18.21 MB 0 B
Bundle Size Analyzer Link Link

@storybook/web-components

Before After Difference
Dependency count 4 4 0
Self size 182 KB 101 KB 🎉 -81 KB 🎉
Dependency size 126 KB 126 KB 0 B
Bundle Size Analyzer Link Link

@valentinpalkovic valentinpalkovic self-assigned this Oct 13, 2026
@valentinpalkovic
valentinpalkovic merged commit fc1e413 into storybookjs:next Oct 13, 2026
164 of 173 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-scan:human bug ci:normal Run our default set of CI jobs (choose this for most PRs). react review:verified Three-step review passed: architecture, code quality, runtime verification sev:S3 Medium priority. Fix within months if possible.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants