Skip to content

Docs: Restore React 16/17 support in useServiceDocgen#35179

Merged
JReinhold merged 3 commits into
nextfrom
jeppe/docs-useservicedocgen-react16-17
Jun 16, 2026
Merged

Docs: Restore React 16/17 support in useServiceDocgen#35179
JReinhold merged 3 commits into
nextfrom
jeppe/docs-useservicedocgen-react16-17

Conversation

@JReinhold

@JReinhold JReinhold commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Closes #

What I did

A recent change added the useServiceDocgen hook to addon-docs, which subscribed docs blocks to the preview's local core/docgen runtime using React's useSyncExternalStore. Using React in docs is fine (docs brings in React even in the framework-agnostic preview), but useSyncExternalStore only exists from React 18 onwards, and addon-docs still supports React 16.8 and 17 (see the react range in code/addons/docs/package.json). This broke those setups.

This PR replaces the useSyncExternalStore call with an inlined shim built from the older hooks (useState, useEffect, useLayoutEffect), while preserving the exact subscription behavior. The shim is a faithful port of React's official use-sync-external-store/shim fallback, which is safe on React 16/17 because those versions render synchronously and therefore can't tear (the tearing problem the real hook guards against only occurs with concurrent rendering, which doesn't exist before React 18).

The shim is clearly commented, including a TODO to delete it and go back to importing useSyncExternalStore from react once support for React < 18 is dropped.

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

Caution

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Manual QA was performed locally against the internal Storybook UI on port 6007 (port 6006 was already in use) with features.experimentalDocgenServer temporarily enabled — that is the code path that exercises useServiceDocgen via the core/docgen open service.

Setup (one-time per QA session)

  1. From the repo root, install dependencies if needed: yarn

  2. Compile core if needed: yarn nx compile core

  3. In code/.storybook/main.ts, temporarily set features.experimentalDocgenServer to true (revert before committing — the internal UI keeps this false by default).

  4. Start the internal Storybook UI on a free port (example uses 6007):

    cd code
    NODE_OPTIONS="--max_old_space_size=4096" node core/dist/bin/dispatcher.js dev --port 6007 --config-dir ./.storybook
  5. Open http://localhost:6007/ in a browser.

Verify docgen renders on the docs page

  1. In the sidebar, expand addons/docs → Examples → Button and open Docs.
  2. Confirm the page loads with no runtime errors in the browser console.
  3. In the docs page content, confirm docgen-driven blocks are populated:
    • Description: shows the component comment, e.g. “Example button component” and “(docs enabled)”.
    • Controls table (top of page): the primary row description reads “Is this the principal call to action on the page?”; other props (label, backgroundColor, size, onClick) also show their JSDoc descriptions.

Verify docgen reacts to source file changes

  1. With the Button docs page open, edit code/addons/docs/src/blocks/examples/Button.tsx.

  2. Change the JSDoc on the primary prop, for example:

    /** QA test: Is this the principal call to action on the page? */
    primary?: boolean;
  3. Save the file. In the Storybook terminal, confirm a Vite HMR line appears, e.g. Vite hmr update /addons/docs/src/blocks/examples/Button.tsx.

  4. In the Storybook sidebar, click Primary (under addons/docs → Examples → Button), then click Docs again.

  5. Confirm the Controls table primary description updated to “QA test: Is this the principal call to action on the page?” without restarting Storybook.

  6. Revert the temporary JSDoc edit in Button.tsx and restore experimentalDocgenServer: false in code/.storybook/main.ts.

React 16/17 regression check (optional, for reviewers with older sandboxes)

  1. Repeat steps 4–13 using a sandbox pinned to React 16 or 17 (daily CI sandboxes). The docs page should render identically and must not throw useSyncExternalStore is not a function.

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.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

Replace the React 18-only useSyncExternalStore call with an inlined shim
built from useState/useEffect/useLayoutEffect, since addon-docs still
supports React 16.8 and 17 which lack that hook. The shim faithfully
mirrors React's official use-sync-external-store fallback and is safe on
React 16/17 (synchronous rendering, no tearing).

Co-authored-by: Cursor <cursoragent@cursor.com>
@JReinhold JReinhold added bug ci:daily Run the CI jobs that normally run in the daily job. labels Jun 16, 2026
@JReinhold JReinhold self-assigned this Jun 16, 2026
@JReinhold JReinhold requested a review from Sidnioulz June 16, 2026 10:10
@JReinhold JReinhold marked this pull request as ready for review June 16, 2026 10:11
@JReinhold JReinhold added the qa:skip Pull Requests that do not need any QA. label Jun 16, 2026
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f57b502b-ba75-4f20-a1cf-65ad2c6711da

📥 Commits

Reviewing files that changed from the base of the PR and between 970de22 and 3370397.

📒 Files selected for processing (1)
  • code/addons/docs/src/blocks/blocks/useServiceDocgen.ts

📝 Walkthrough

Walkthrough

useServiceDocgen replaces its import of React's useSyncExternalStore with useEffect and useLayoutEffect, and introduces an inline useSyncExternalStoreShim. The shim reads the snapshot during render, uses useLayoutEffect to detect snapshot changes before commit, and useEffect to manage subscription and post-commit changes. A didSnapshotChange helper compares snapshots via Object.is with an exception fallback.

Changes

Inline useSyncExternalStore shim for useServiceDocgen

Layer / File(s) Summary
Import update and useSyncExternalStoreShim implementation
code/addons/docs/src/blocks/blocks/useServiceDocgen.ts
Swaps the useSyncExternalStore import for useEffect/useLayoutEffect, adds the inline useSyncExternalStoreShim with a didSnapshotChange helper, and updates useServiceDocgen to call the shim instead of the React built-in.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@Sidnioulz Sidnioulz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks!

@JReinhold JReinhold merged commit ff9d956 into next Jun 16, 2026
319 of 321 checks passed
@JReinhold JReinhold deleted the jeppe/docs-useservicedocgen-react16-17 branch June 16, 2026 20:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addon: docs bug ci:daily Run the CI jobs that normally run in the daily job. qa:skip Pull Requests that do not need any QA.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants