From c49208c1edeb65ce6a99623e3fa639fb7f7d8bc7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:40:30 +0000 Subject: [PATCH 1/5] Version Packages --- .../anchored-position-scroll-recalculation.md | 5 ---- .changeset/bumpy-paws-end.md | 5 ---- .changeset/combined-refs-hook.md | 5 ---- .../graduate-css-has-selector-perf-flag.md | 5 ---- .changeset/improve-theme-provider-perf.md | 11 -------- .changeset/soft-pianos-carry.md | 5 ---- .changeset/stack-spacing-options.md | 5 ---- .changeset/tough-ears-drive.md | 5 ---- examples/codesandbox/package.json | 4 +-- examples/nextjs/package.json | 4 +-- examples/theming/package.json | 4 +-- packages/react/CHANGELOG.md | 27 +++++++++++++++++++ packages/react/package.json | 2 +- packages/styled-react/CHANGELOG.md | 11 ++++++++ packages/styled-react/package.json | 4 +-- 15 files changed, 47 insertions(+), 55 deletions(-) delete mode 100644 .changeset/anchored-position-scroll-recalculation.md delete mode 100644 .changeset/bumpy-paws-end.md delete mode 100644 .changeset/combined-refs-hook.md delete mode 100644 .changeset/graduate-css-has-selector-perf-flag.md delete mode 100644 .changeset/improve-theme-provider-perf.md delete mode 100644 .changeset/soft-pianos-carry.md delete mode 100644 .changeset/stack-spacing-options.md delete mode 100644 .changeset/tough-ears-drive.md diff --git a/.changeset/anchored-position-scroll-recalculation.md b/.changeset/anchored-position-scroll-recalculation.md deleted file mode 100644 index e29a6819a1b..00000000000 --- a/.changeset/anchored-position-scroll-recalculation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -useAnchoredPosition: recalculate overlay position when any scrollable ancestor (or the window) is scrolled. diff --git a/.changeset/bumpy-paws-end.md b/.changeset/bumpy-paws-end.md deleted file mode 100644 index 3ac790105b4..00000000000 --- a/.changeset/bumpy-paws-end.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Fix anchor-name not being set on the anchor element when SelectPanel is opened in Copilot code agent. diff --git a/.changeset/combined-refs-hook.md b/.changeset/combined-refs-hook.md deleted file mode 100644 index e8e9c96e0cc..00000000000 --- a/.changeset/combined-refs-hook.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Update internal implementations of combined refs to improve performance and add support for React 19 callback refs diff --git a/.changeset/graduate-css-has-selector-perf-flag.md b/.changeset/graduate-css-has-selector-perf-flag.md deleted file mode 100644 index 0aa702353a7..00000000000 --- a/.changeset/graduate-css-has-selector-perf-flag.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling diff --git a/.changeset/improve-theme-provider-perf.md b/.changeset/improve-theme-provider-perf.md deleted file mode 100644 index 93ae86578ad..00000000000 --- a/.changeset/improve-theme-provider-perf.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@primer/react": patch -"@primer/styled-react": patch ---- - -perf(ThemeProvider): Reduce unnecessary renders and effect cascades - -- Replace `useState` + `useEffect` SSR hydration handoff with `useSyncExternalStore` — eliminates post-hydration re-render -- Replace `useState` + `useEffect` in `useSystemColorMode` with `useSyncExternalStore` — eliminates effect gap and stale-then-update flicker -- Cache `getServerHandoff` DOM read + JSON.parse per ID (runs once, not on every call) -- Memoize context value object to prevent unnecessary re-renders of all consumers diff --git a/.changeset/soft-pianos-carry.md b/.changeset/soft-pianos-carry.md deleted file mode 100644 index bebeac93431..00000000000 --- a/.changeset/soft-pianos-carry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': minor ---- - -AnchoredOverlay: Add Popover API to AnchoredOverlay (behind `primer_react_css_anchor_positioning` feature flag) diff --git a/.changeset/stack-spacing-options.md b/.changeset/stack-spacing-options.md deleted file mode 100644 index 40610098a23..00000000000 --- a/.changeset/stack-spacing-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': minor ---- - -**Stack**: Add `tight` (4px) and `cozy` (12px) spacing values to `gap` and `padding` props. Add `paddingBlock` and `paddingInline` props for directional padding control. diff --git a/.changeset/tough-ears-drive.md b/.changeset/tough-ears-drive.md deleted file mode 100644 index 7cb54e4143e..00000000000 --- a/.changeset/tough-ears-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -ActionList.Item: fix inline descriptions being referenced via `aria-labelledby` instead of `aria-describedby` diff --git a/examples/codesandbox/package.json b/examples/codesandbox/package.json index 9c69af6c8d5..792cdc70530 100644 --- a/examples/codesandbox/package.json +++ b/examples/codesandbox/package.json @@ -17,8 +17,8 @@ "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.3", - "@primer/react": "38.18.0", - "@primer/styled-react": "1.0.4", + "@primer/react": "38.19.0", + "@primer/styled-react": "1.0.5", "styled-components": "5.x", "typescript": "^5.9.2", "vite": "^7.1.11" diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index dd9a1baac0a..6c598a68122 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -10,8 +10,8 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@primer/react": "38.18.0", - "@primer/styled-react": "1.0.4", + "@primer/react": "38.19.0", + "@primer/styled-react": "1.0.5", "next": "^16.1.7", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/theming/package.json b/examples/theming/package.json index 678befedc86..12c4702d17d 100644 --- a/examples/theming/package.json +++ b/examples/theming/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@primer/octicons-react": "^19.21.0", - "@primer/react": "38.18.0", - "@primer/styled-react": "1.0.4", + "@primer/react": "38.19.0", + "@primer/styled-react": "1.0.5", "clsx": "^2.1.1", "next": "^16.1.7", "react": "^19.2.0", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 41247e0c598..c7a224a13c9 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,32 @@ # @primer/react +## 38.19.0 + +### Minor Changes + +- [#7677](https://github.com/primer/react/pull/7677) [`c1a81b1`](https://github.com/primer/react/commit/c1a81b178742ba547b85a3df3ed3c27bcff6b7c5) Thanks [@TylerJDev](https://github.com/TylerJDev)! - AnchoredOverlay: Add Popover API to AnchoredOverlay (behind `primer_react_css_anchor_positioning` feature flag) + +- [#7697](https://github.com/primer/react/pull/7697) [`990ce7b`](https://github.com/primer/react/commit/990ce7b625bcf90ef3867e93086d0c74835a9068) Thanks [@hectahertz](https://github.com/hectahertz)! - **Stack**: Add `tight` (4px) and `cozy` (12px) spacing values to `gap` and `padding` props. Add `paddingBlock` and `paddingInline` props for directional padding control. + +### Patch Changes + +- [#7652](https://github.com/primer/react/pull/7652) [`5d19e2b`](https://github.com/primer/react/commit/5d19e2bb5bbd444f2e82d98eef300c221863941c) Thanks [@owenniblock](https://github.com/owenniblock)! - useAnchoredPosition: recalculate overlay position when any scrollable ancestor (or the window) is scrolled. + +- [#7707](https://github.com/primer/react/pull/7707) [`6431bfe`](https://github.com/primer/react/commit/6431bfecd72e24db1dfa90b02f8a42834e63843b) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Fix anchor-name not being set on the anchor element when SelectPanel is opened in Copilot code agent. + +- [#7638](https://github.com/primer/react/pull/7638) [`f04e85d`](https://github.com/primer/react/commit/f04e85df536cd6e8047f70b070bd3cf7c0961f92) Thanks [@iansan5653](https://github.com/iansan5653)! - Update internal implementations of combined refs to improve performance and add support for React 19 callback refs + +- [#7633](https://github.com/primer/react/pull/7633) [`a107d39`](https://github.com/primer/react/commit/a107d398e0574b5f8085485c96b27a168061eb50) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling + +- [#7695](https://github.com/primer/react/pull/7695) [`780fc3d`](https://github.com/primer/react/commit/780fc3d7b52fd0f9b63f313af6355398180a0118) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(ThemeProvider): Reduce unnecessary renders and effect cascades + + - Replace `useState` + `useEffect` SSR hydration handoff with `useSyncExternalStore` — eliminates post-hydration re-render + - Replace `useState` + `useEffect` in `useSystemColorMode` with `useSyncExternalStore` — eliminates effect gap and stale-then-update flicker + - Cache `getServerHandoff` DOM read + JSON.parse per ID (runs once, not on every call) + - Memoize context value object to prevent unnecessary re-renders of all consumers + +- [#7706](https://github.com/primer/react/pull/7706) [`fd8910a`](https://github.com/primer/react/commit/fd8910abff851f43d19805ceaa439a9a18c4f226) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - ActionList.Item: fix inline descriptions being referenced via `aria-labelledby` instead of `aria-describedby` + ## 38.18.0 ### Minor Changes diff --git a/packages/react/package.json b/packages/react/package.json index d4ae30e6638..eca897e2ae3 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,7 +1,7 @@ { "name": "@primer/react", "type": "module", - "version": "38.18.0", + "version": "38.19.0", "description": "An implementation of GitHub's Primer Design System using React", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/packages/styled-react/CHANGELOG.md b/packages/styled-react/CHANGELOG.md index 804dfbb52a7..954c5ae2c71 100644 --- a/packages/styled-react/CHANGELOG.md +++ b/packages/styled-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @primer/styled-react +## 1.0.5 + +### Patch Changes + +- [#7695](https://github.com/primer/react/pull/7695) [`780fc3d`](https://github.com/primer/react/commit/780fc3d7b52fd0f9b63f313af6355398180a0118) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(ThemeProvider): Reduce unnecessary renders and effect cascades + + - Replace `useState` + `useEffect` SSR hydration handoff with `useSyncExternalStore` — eliminates post-hydration re-render + - Replace `useState` + `useEffect` in `useSystemColorMode` with `useSyncExternalStore` — eliminates effect gap and stale-then-update flicker + - Cache `getServerHandoff` DOM read + JSON.parse per ID (runs once, not on every call) + - Memoize context value object to prevent unnecessary re-renders of all consumers + ## 1.0.4 ### Patch Changes diff --git a/packages/styled-react/package.json b/packages/styled-react/package.json index 4e11690127a..605620e63bc 100644 --- a/packages/styled-react/package.json +++ b/packages/styled-react/package.json @@ -1,6 +1,6 @@ { "name": "@primer/styled-react", - "version": "1.0.4", + "version": "1.0.5", "type": "module", "exports": { ".": { @@ -49,7 +49,7 @@ "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", "@primer/primitives": "10.x || 11.x", - "@primer/react": "^38.18.0", + "@primer/react": "^38.19.0", "@rollup/plugin-babel": "^6.1.0", "@storybook/react-vite": "^10.3.3", "@types/react": "18.3.11", From d5ce39f1e8d536d7adbba54710dcf8a6092b2089 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Wed, 1 Apr 2026 14:50:21 -0700 Subject: [PATCH 2/5] copilot->mattcosta7 --- packages/react/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index c7a224a13c9..28f18506a01 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -16,7 +16,7 @@ - [#7638](https://github.com/primer/react/pull/7638) [`f04e85d`](https://github.com/primer/react/commit/f04e85df536cd6e8047f70b070bd3cf7c0961f92) Thanks [@iansan5653](https://github.com/iansan5653)! - Update internal implementations of combined refs to improve performance and add support for React 19 callback refs -- [#7633](https://github.com/primer/react/pull/7633) [`a107d39`](https://github.com/primer/react/commit/a107d398e0574b5f8085485c96b27a168061eb50) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling +- [#7633](https://github.com/primer/react/pull/7633) [`a107d39`](https://github.com/primer/react/commit/a107d398e0574b5f8085485c96b27a168061eb50) Thanks [@mattcosta7](https://github.com/mattcosta7)! - Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling - [#7695](https://github.com/primer/react/pull/7695) [`780fc3d`](https://github.com/primer/react/commit/780fc3d7b52fd0f9b63f313af6355398180a0118) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(ThemeProvider): Reduce unnecessary renders and effect cascades From befbd48534eb477ac2d88cf4ceea3ac426029447 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:41:46 +0000 Subject: [PATCH 3/5] Version Packages --- .../anchored-position-scroll-recalculation.md | 5 ---- .changeset/bumpy-paws-end.md | 5 ---- .changeset/combined-refs-hook.md | 5 ---- .../graduate-css-has-selector-perf-flag.md | 5 ---- .changeset/improve-theme-provider-perf.md | 11 -------- .changeset/soft-pianos-carry.md | 5 ---- .changeset/stack-spacing-options.md | 5 ---- .changeset/tough-ears-drive.md | 5 ---- examples/codesandbox/package.json | 4 +-- examples/nextjs/package.json | 4 +-- examples/theming/package.json | 4 +-- packages/react/CHANGELOG.md | 27 +++++++++++++++++++ packages/react/package.json | 2 +- packages/styled-react/CHANGELOG.md | 11 ++++++++ packages/styled-react/package.json | 4 +-- 15 files changed, 47 insertions(+), 55 deletions(-) delete mode 100644 .changeset/anchored-position-scroll-recalculation.md delete mode 100644 .changeset/bumpy-paws-end.md delete mode 100644 .changeset/combined-refs-hook.md delete mode 100644 .changeset/graduate-css-has-selector-perf-flag.md delete mode 100644 .changeset/improve-theme-provider-perf.md delete mode 100644 .changeset/soft-pianos-carry.md delete mode 100644 .changeset/stack-spacing-options.md delete mode 100644 .changeset/tough-ears-drive.md diff --git a/.changeset/anchored-position-scroll-recalculation.md b/.changeset/anchored-position-scroll-recalculation.md deleted file mode 100644 index e29a6819a1b..00000000000 --- a/.changeset/anchored-position-scroll-recalculation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -useAnchoredPosition: recalculate overlay position when any scrollable ancestor (or the window) is scrolled. diff --git a/.changeset/bumpy-paws-end.md b/.changeset/bumpy-paws-end.md deleted file mode 100644 index 3ac790105b4..00000000000 --- a/.changeset/bumpy-paws-end.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Fix anchor-name not being set on the anchor element when SelectPanel is opened in Copilot code agent. diff --git a/.changeset/combined-refs-hook.md b/.changeset/combined-refs-hook.md deleted file mode 100644 index e8e9c96e0cc..00000000000 --- a/.changeset/combined-refs-hook.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Update internal implementations of combined refs to improve performance and add support for React 19 callback refs diff --git a/.changeset/graduate-css-has-selector-perf-flag.md b/.changeset/graduate-css-has-selector-perf-flag.md deleted file mode 100644 index 0aa702353a7..00000000000 --- a/.changeset/graduate-css-has-selector-perf-flag.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling diff --git a/.changeset/improve-theme-provider-perf.md b/.changeset/improve-theme-provider-perf.md deleted file mode 100644 index 93ae86578ad..00000000000 --- a/.changeset/improve-theme-provider-perf.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@primer/react": patch -"@primer/styled-react": patch ---- - -perf(ThemeProvider): Reduce unnecessary renders and effect cascades - -- Replace `useState` + `useEffect` SSR hydration handoff with `useSyncExternalStore` — eliminates post-hydration re-render -- Replace `useState` + `useEffect` in `useSystemColorMode` with `useSyncExternalStore` — eliminates effect gap and stale-then-update flicker -- Cache `getServerHandoff` DOM read + JSON.parse per ID (runs once, not on every call) -- Memoize context value object to prevent unnecessary re-renders of all consumers diff --git a/.changeset/soft-pianos-carry.md b/.changeset/soft-pianos-carry.md deleted file mode 100644 index bebeac93431..00000000000 --- a/.changeset/soft-pianos-carry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': minor ---- - -AnchoredOverlay: Add Popover API to AnchoredOverlay (behind `primer_react_css_anchor_positioning` feature flag) diff --git a/.changeset/stack-spacing-options.md b/.changeset/stack-spacing-options.md deleted file mode 100644 index 40610098a23..00000000000 --- a/.changeset/stack-spacing-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': minor ---- - -**Stack**: Add `tight` (4px) and `cozy` (12px) spacing values to `gap` and `padding` props. Add `paddingBlock` and `paddingInline` props for directional padding control. diff --git a/.changeset/tough-ears-drive.md b/.changeset/tough-ears-drive.md deleted file mode 100644 index 7cb54e4143e..00000000000 --- a/.changeset/tough-ears-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -ActionList.Item: fix inline descriptions being referenced via `aria-labelledby` instead of `aria-describedby` diff --git a/examples/codesandbox/package.json b/examples/codesandbox/package.json index 9c69af6c8d5..792cdc70530 100644 --- a/examples/codesandbox/package.json +++ b/examples/codesandbox/package.json @@ -17,8 +17,8 @@ "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.3", - "@primer/react": "38.18.0", - "@primer/styled-react": "1.0.4", + "@primer/react": "38.19.0", + "@primer/styled-react": "1.0.5", "styled-components": "5.x", "typescript": "^5.9.2", "vite": "^7.1.11" diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index dd9a1baac0a..6c598a68122 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -10,8 +10,8 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@primer/react": "38.18.0", - "@primer/styled-react": "1.0.4", + "@primer/react": "38.19.0", + "@primer/styled-react": "1.0.5", "next": "^16.1.7", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/theming/package.json b/examples/theming/package.json index 678befedc86..12c4702d17d 100644 --- a/examples/theming/package.json +++ b/examples/theming/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@primer/octicons-react": "^19.21.0", - "@primer/react": "38.18.0", - "@primer/styled-react": "1.0.4", + "@primer/react": "38.19.0", + "@primer/styled-react": "1.0.5", "clsx": "^2.1.1", "next": "^16.1.7", "react": "^19.2.0", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 41247e0c598..c7a224a13c9 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,32 @@ # @primer/react +## 38.19.0 + +### Minor Changes + +- [#7677](https://github.com/primer/react/pull/7677) [`c1a81b1`](https://github.com/primer/react/commit/c1a81b178742ba547b85a3df3ed3c27bcff6b7c5) Thanks [@TylerJDev](https://github.com/TylerJDev)! - AnchoredOverlay: Add Popover API to AnchoredOverlay (behind `primer_react_css_anchor_positioning` feature flag) + +- [#7697](https://github.com/primer/react/pull/7697) [`990ce7b`](https://github.com/primer/react/commit/990ce7b625bcf90ef3867e93086d0c74835a9068) Thanks [@hectahertz](https://github.com/hectahertz)! - **Stack**: Add `tight` (4px) and `cozy` (12px) spacing values to `gap` and `padding` props. Add `paddingBlock` and `paddingInline` props for directional padding control. + +### Patch Changes + +- [#7652](https://github.com/primer/react/pull/7652) [`5d19e2b`](https://github.com/primer/react/commit/5d19e2bb5bbd444f2e82d98eef300c221863941c) Thanks [@owenniblock](https://github.com/owenniblock)! - useAnchoredPosition: recalculate overlay position when any scrollable ancestor (or the window) is scrolled. + +- [#7707](https://github.com/primer/react/pull/7707) [`6431bfe`](https://github.com/primer/react/commit/6431bfecd72e24db1dfa90b02f8a42834e63843b) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Fix anchor-name not being set on the anchor element when SelectPanel is opened in Copilot code agent. + +- [#7638](https://github.com/primer/react/pull/7638) [`f04e85d`](https://github.com/primer/react/commit/f04e85df536cd6e8047f70b070bd3cf7c0961f92) Thanks [@iansan5653](https://github.com/iansan5653)! - Update internal implementations of combined refs to improve performance and add support for React 19 callback refs + +- [#7633](https://github.com/primer/react/pull/7633) [`a107d39`](https://github.com/primer/react/commit/a107d398e0574b5f8085485c96b27a168061eb50) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling + +- [#7695](https://github.com/primer/react/pull/7695) [`780fc3d`](https://github.com/primer/react/commit/780fc3d7b52fd0f9b63f313af6355398180a0118) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(ThemeProvider): Reduce unnecessary renders and effect cascades + + - Replace `useState` + `useEffect` SSR hydration handoff with `useSyncExternalStore` — eliminates post-hydration re-render + - Replace `useState` + `useEffect` in `useSystemColorMode` with `useSyncExternalStore` — eliminates effect gap and stale-then-update flicker + - Cache `getServerHandoff` DOM read + JSON.parse per ID (runs once, not on every call) + - Memoize context value object to prevent unnecessary re-renders of all consumers + +- [#7706](https://github.com/primer/react/pull/7706) [`fd8910a`](https://github.com/primer/react/commit/fd8910abff851f43d19805ceaa439a9a18c4f226) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - ActionList.Item: fix inline descriptions being referenced via `aria-labelledby` instead of `aria-describedby` + ## 38.18.0 ### Minor Changes diff --git a/packages/react/package.json b/packages/react/package.json index d4ae30e6638..eca897e2ae3 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,7 +1,7 @@ { "name": "@primer/react", "type": "module", - "version": "38.18.0", + "version": "38.19.0", "description": "An implementation of GitHub's Primer Design System using React", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/packages/styled-react/CHANGELOG.md b/packages/styled-react/CHANGELOG.md index 804dfbb52a7..954c5ae2c71 100644 --- a/packages/styled-react/CHANGELOG.md +++ b/packages/styled-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @primer/styled-react +## 1.0.5 + +### Patch Changes + +- [#7695](https://github.com/primer/react/pull/7695) [`780fc3d`](https://github.com/primer/react/commit/780fc3d7b52fd0f9b63f313af6355398180a0118) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(ThemeProvider): Reduce unnecessary renders and effect cascades + + - Replace `useState` + `useEffect` SSR hydration handoff with `useSyncExternalStore` — eliminates post-hydration re-render + - Replace `useState` + `useEffect` in `useSystemColorMode` with `useSyncExternalStore` — eliminates effect gap and stale-then-update flicker + - Cache `getServerHandoff` DOM read + JSON.parse per ID (runs once, not on every call) + - Memoize context value object to prevent unnecessary re-renders of all consumers + ## 1.0.4 ### Patch Changes diff --git a/packages/styled-react/package.json b/packages/styled-react/package.json index 4e11690127a..605620e63bc 100644 --- a/packages/styled-react/package.json +++ b/packages/styled-react/package.json @@ -1,6 +1,6 @@ { "name": "@primer/styled-react", - "version": "1.0.4", + "version": "1.0.5", "type": "module", "exports": { ".": { @@ -49,7 +49,7 @@ "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", "@primer/primitives": "10.x || 11.x", - "@primer/react": "^38.18.0", + "@primer/react": "^38.19.0", "@rollup/plugin-babel": "^6.1.0", "@storybook/react-vite": "^10.3.3", "@types/react": "18.3.11", From e473f0ad76051fa796613244852ad0cfd9073111 Mon Sep 17 00:00:00 2001 From: LiuLiu Date: Fri, 3 Apr 2026 11:54:45 -0700 Subject: [PATCH 4/5] Update CHANGELOG --- packages/react/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index c7a224a13c9..28f18506a01 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -16,7 +16,7 @@ - [#7638](https://github.com/primer/react/pull/7638) [`f04e85d`](https://github.com/primer/react/commit/f04e85df536cd6e8047f70b070bd3cf7c0961f92) Thanks [@iansan5653](https://github.com/iansan5653)! - Update internal implementations of combined refs to improve performance and add support for React 19 callback refs -- [#7633](https://github.com/primer/react/pull/7633) [`a107d39`](https://github.com/primer/react/commit/a107d398e0574b5f8085485c96b27a168061eb50) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling +- [#7633](https://github.com/primer/react/pull/7633) [`a107d39`](https://github.com/primer/react/commit/a107d398e0574b5f8085485c96b27a168061eb50) Thanks [@mattcosta7](https://github.com/mattcosta7)! - Graduate `primer_react_css_has_selector_perf` feature flag: the CSS `:has()` performance optimization (`body[data-dialog-scroll-disabled]`) is now the default behavior for Dialog scroll disabling - [#7695](https://github.com/primer/react/pull/7695) [`780fc3d`](https://github.com/primer/react/commit/780fc3d7b52fd0f9b63f313af6355398180a0118) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(ThemeProvider): Reduce unnecessary renders and effect cascades From 7b0c3978c653e125ba2f050a5645d4d92bbf9d50 Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Wed, 8 Apr 2026 09:01:45 -0700 Subject: [PATCH 5/5] Revert "Replace `useRefObjectAsForwardedRef` with `useMergedRefs` internally (#7638)" This reverts commit f04e85df536cd6e8047f70b070bd3cf7c0961f92. --- packages/react/src/ActionBar/ActionBar.tsx | 6 +++--- packages/react/src/ActionList/Heading.tsx | 6 +++--- packages/react/src/Autocomplete/Autocomplete.test.tsx | 1 - packages/react/src/Autocomplete/AutocompleteInput.tsx | 6 +++--- .../react/src/Autocomplete/AutocompleteOverlay.tsx | 6 +++--- packages/react/src/Button/ButtonBase.tsx | 7 ++++--- packages/react/src/Dialog/Dialog.tsx | 6 +++--- packages/react/src/Heading/Heading.tsx | 6 +++--- packages/react/src/Link/Link.tsx | 7 ++++--- packages/react/src/Overlay/Overlay.tsx | 6 +++--- packages/react/src/PageLayout/PageLayout.tsx | 10 +++++----- .../src/TextInputWithTokens/TextInputWithTokens.tsx | 6 +++--- packages/react/src/deprecated/DialogV1/Dialog.tsx | 6 +++--- .../react/src/hooks/__tests__/useMergedRefs.test.tsx | 4 ++-- packages/react/src/hooks/useMergedRefs.ts | 8 ++++---- 15 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/react/src/ActionBar/ActionBar.tsx b/packages/react/src/ActionBar/ActionBar.tsx index 2ba3b086b9c..83712ce21fd 100644 --- a/packages/react/src/ActionBar/ActionBar.tsx +++ b/packages/react/src/ActionBar/ActionBar.tsx @@ -12,7 +12,7 @@ import {ActionMenu} from '../ActionMenu' import {useFocusZone, FocusKeys} from '../hooks/useFocusZone' import styles from './ActionBar.module.css' import {clsx} from 'clsx' -import {useMergedRefs} from '../hooks' +import {useRefObjectAsForwardedRef} from '../hooks' import {createDescendantRegistry} from '../utils/descendant-registry' const ACTIONBAR_ITEM_GAP = 8 @@ -470,7 +470,7 @@ function useWidth(ref: React.RefObject) { export const ActionBarIconButton = forwardRef( ({disabled, onClick, ...props}: ActionBarIconButtonProps, forwardedRef) => { const ref = useRef(null) - const mergedRef = useMergedRefs(ref, forwardedRef) + useRefObjectAsForwardedRef(forwardedRef, ref) const {size, isVisibleChild} = React.useContext(ActionBarContext) const {groupId} = React.useContext(ActionBarGroupContext) @@ -507,7 +507,7 @@ export const ActionBarIconButton = forwardRef( return ( { const innerRef = React.useRef(null) - const mergedRef = useMergedRefs(forwardedRef, innerRef) + useRefObjectAsForwardedRef(forwardedRef, innerRef) const {headingId: headingId, variant: listVariant} = React.useContext(ListContext) const {container} = React.useContext(ActionListContainerContext) @@ -37,7 +37,7 @@ export const Heading = forwardRef(({as, size, children, visuallyHidden = false, { const inputNode = getByLabelText(AUTOCOMPLETE_LABEL) expect(inputNode.getAttribute('aria-expanded')).not.toBe('true') - inputNode.focus() fireEvent.click(inputNode) fireEvent.keyDown(inputNode, {key: 'ArrowDown'}) diff --git a/packages/react/src/Autocomplete/AutocompleteInput.tsx b/packages/react/src/Autocomplete/AutocompleteInput.tsx index ef1278451a1..61b2905e5ba 100644 --- a/packages/react/src/Autocomplete/AutocompleteInput.tsx +++ b/packages/react/src/Autocomplete/AutocompleteInput.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useContext, useEffect, useState} from 'react' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import {AutocompleteContext, AutocompleteInputContext} from './AutocompleteContext' import TextInput from '../TextInput' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import type {ComponentProps} from '../utils/types' import useSafeTimeout from '../hooks/useSafeTimeout' @@ -43,7 +43,7 @@ const AutocompleteInput = React.forwardRef( } const {activeDescendantRef, id, inputRef, setInputValue, setShowMenu, showMenu} = autocompleteContext const {autocompleteSuggestion = '', inputValue = '', isMenuDirectlyActivated} = inputContext - const mergedRef = useMergedRefs(forwardedRef, inputRef) + useRefObjectAsForwardedRef(forwardedRef, inputRef) const [highlightRemainingText, setHighlightRemainingText] = useState(true) const {safeSetTimeout} = useSafeTimeout() @@ -160,7 +160,7 @@ const AutocompleteInput = React.forwardRef( onKeyDown={handleInputKeyDown} onKeyPress={onInputKeyPress} onKeyUp={handleInputKeyUp} - ref={mergedRef} + ref={inputRef} aria-controls={`${id}-listbox`} aria-autocomplete="both" role="combobox" diff --git a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx index 9d810b90f75..0755434225e 100644 --- a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx +++ b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx @@ -5,7 +5,7 @@ import type {OverlayProps} from '../Overlay' import Overlay from '../Overlay' import type {ComponentProps} from '../utils/types' import {AutocompleteContext} from './AutocompleteContext' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import VisuallyHidden from '../_VisuallyHidden' import classes from './AutocompleteOverlay.module.css' @@ -57,7 +57,7 @@ function AutocompleteOverlay({ [showMenu, selectedItemLength], ) - const mergedRef = useMergedRefs(scrollContainerRef, floatingElementRef) + useRefObjectAsForwardedRef(scrollContainerRef, floatingElementRef) const closeOptionList = useCallback(() => { setShowMenu(false) @@ -73,7 +73,7 @@ function AutocompleteOverlay({ preventFocusOnOpen={true} onClickOutside={closeOptionList} onEscape={closeOptionList} - ref={mergedRef} + ref={floatingElementRef as React.RefObject} top={position?.top} left={position?.left} className={clsx(classes.Overlay, className)} diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index 8642e7a04f4..9600a6f86b8 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -1,7 +1,7 @@ import React, {forwardRef, type JSX} from 'react' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import type {ButtonProps} from './types' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import {VisuallyHidden} from '../VisuallyHidden' import Spinner from '../Spinner' import CounterLabel from '../CounterLabel' @@ -51,7 +51,7 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f } = props const innerRef = React.useRef(null) - const combinedRefs = useMergedRefs(forwardedRef, innerRef) + useRefObjectAsForwardedRef(forwardedRef, innerRef) const uuid = useId(id) const loadingAnnouncementID = `${uuid}-loading-announcement` @@ -87,7 +87,8 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f (null) - const mergedRef = useMergedRefs(forwardedRef, dialogRef) + useRefObjectAsForwardedRef(forwardedRef, dialogRef) const backdropRef = useRef(null) useFocusTrap({ @@ -388,7 +388,7 @@ const _Dialog = React.forwardRef
{ const innerRef = React.useRef(null) - const mergedRef = useMergedRefs(forwardedRef, innerRef) + useRefObjectAsForwardedRef(forwardedRef, innerRef) if (__DEV__) { /** @@ -32,7 +32,7 @@ const Heading = forwardRef(({as: Component = 'h2', className, variant, ...props} }, [innerRef]) } - return + return }) as PolymorphicForwardRefComponent Heading.displayName = 'Heading' diff --git a/packages/react/src/Link/Link.tsx b/packages/react/src/Link/Link.tsx index 574f9a38cef..65d08ac1424 100644 --- a/packages/react/src/Link/Link.tsx +++ b/packages/react/src/Link/Link.tsx @@ -1,6 +1,6 @@ import {clsx} from 'clsx' import React, {useEffect, type ForwardedRef, type ElementRef} from 'react' -import {useMergedRefs} from '../hooks' +import {useRefObjectAsForwardedRef} from '../hooks' import classes from './Link.module.css' import type {ComponentProps} from '../utils/types' import {type PolymorphicProps, fixedForwardRef} from '../utils/modern-polymorphic' @@ -20,7 +20,7 @@ export const UnwrappedLink = ( ) => { const {as: Component = 'a', className, inline, muted, hoverColor, ...restProps} = props const innerRef = React.useRef>(null) - const mergedRef = useMergedRefs(ref, innerRef) + useRefObjectAsForwardedRef(ref, innerRef) if (__DEV__) { /** @@ -53,7 +53,8 @@ export const UnwrappedLink = ( data-inline={inline} data-hover-color={hoverColor} {...restProps} - ref={mergedRef} + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ref={innerRef as any} /> ) } diff --git a/packages/react/src/Overlay/Overlay.tsx b/packages/react/src/Overlay/Overlay.tsx index f495d015730..0ba9d556419 100644 --- a/packages/react/src/Overlay/Overlay.tsx +++ b/packages/react/src/Overlay/Overlay.tsx @@ -5,7 +5,7 @@ import type {AriaRole, Merge} from '../utils/types' import type {TouchOrMouseEvent} from '../hooks' import {useOverlay} from '../hooks' import Portal from '../Portal' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import type {AnchorSide} from '@primer/behaviors' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import classes from './Overlay.module.css' @@ -194,7 +194,7 @@ const Overlay = React.forwardRef( ): ReactElement => { const featureFlagMaxHeightClampToViewport = useFeatureFlag('primer_react_overlay_max_height_clamp_to_viewport') const overlayRef = useRef(null) - const mergedRef = useMergedRefs(forwardedRef, overlayRef) + useRefObjectAsForwardedRef(forwardedRef, overlayRef) const slideAnimationDistance = 8 // var(--base-size-8), hardcoded to do some math const slideAnimationEasing = 'cubic-bezier(0.33, 1, 0.68, 1)' const cssAnchorPositioning = useFeatureFlag('primer_react_css_anchor_positioning') @@ -239,7 +239,7 @@ const Overlay = React.forwardRef( role={role} width={width} data-reflow-container={!preventOverflow ? true : undefined} - ref={mergedRef} + ref={overlayRef} left={leftPosition} right={right} height={height} diff --git a/packages/react/src/PageLayout/PageLayout.tsx b/packages/react/src/PageLayout/PageLayout.tsx index 0cee74e68a4..97946a17f55 100644 --- a/packages/react/src/PageLayout/PageLayout.tsx +++ b/packages/react/src/PageLayout/PageLayout.tsx @@ -1,7 +1,7 @@ import React, {memo, useRef} from 'react' import {clsx} from 'clsx' import {useId} from '../hooks/useId' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import type {ResponsiveValue} from '../hooks/useResponsiveValue' import {isResponsiveValue} from '../hooks/useResponsiveValue' import {useSlots} from '../hooks/useSlots' @@ -838,7 +838,7 @@ const Pane = React.forwardRef
)}
(null) const selectedValuesDescriptionId = useId() - const mergedRef = useMergedRefs(forwardedRef, ref) + useRefObjectAsForwardedRef(forwardedRef, ref) const [selectedTokenIndex, setSelectedTokenIndex] = useState() const [tokensAreTruncated, setTokensAreTruncated] = useState(Boolean(visibleTokenCount)) const selectedTokenTexts = tokens @@ -310,7 +310,7 @@ function TextInputWithTokensInnerComponent
( ) => { const overlayRef = useRef(null) const modalRef = useRef(null) - const mergedRef = useMergedRefs(forwardedRef, modalRef) + useRefObjectAsForwardedRef(forwardedRef, modalRef) const closeButtonRef = useRef(null) const onCloseClick = () => { @@ -73,7 +73,7 @@ const Dialog = forwardRef( const Component = forwardRef(({asButton}, forwardedRef) => { const ref: InputOrButtonRef = React.useRef(null) - const mergedRef = useMergedRefs(forwardedRef, ref) + const combinedRef = useMergedRefs(forwardedRef, ref) - return asButton ?