Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
759fa03
upgrade to react 19.2
mhornbacher Dec 8, 2025
8df5785
add failing tests
mhornbacher Dec 8, 2025
07cd96d
fix Activity with single re-render on minimal overhead
mhornbacher Dec 8, 2025
69b7049
fix: linter
mhornbacher Dec 8, 2025
06b214e
fix react version
mhornbacher Dec 8, 2025
f10551e
add @storybook/blocks to resolve build issues
mhornbacher Dec 8, 2025
fb1d167
Merge branch 'main' into fix-activities-with-collections
mhornbacher Dec 8, 2025
041187a
normalize imports
mhornbacher Dec 8, 2025
a5a20ad
diagnose ci issue caused by package changes
mhornbacher Dec 8, 2025
2743617
add additional logging
mhornbacher Dec 8, 2025
5fd02eb
Merge branch 'main' into fix-activities-with-collections
snowystinger Dec 9, 2025
24cb76e
fix packages
snowystinger Dec 9, 2025
f676aa8
Merge branch 'main' into fix-activities-with-collections
mhornbacher Dec 9, 2025
0b25078
Merge branch 'main' into fix-activities-with-collections
mhornbacher Dec 10, 2025
f85ca16
Merge branch 'main' into fix-activities-with-collections
mhornbacher Dec 10, 2025
08c1e24
Merge remote-tracking branch 'origin/main' into fix-activities-with-c…
mhornbacher Dec 11, 2025
e644b52
Merge branch 'main' into fix-activities-with-collections
mhornbacher Dec 15, 2025
28f5ae8
Merge branch 'main' into fix-activities-with-collections
mhornbacher Dec 15, 2025
a9f03bb
Merge branch 'main' into fix-activities-with-collections
snowystinger Dec 17, 2025
4ee0866
Merge branch 'main' into fix-activities-with-collections
mhornbacher Jan 8, 2026
d514d55
Merge branch 'main' into fix-activities-with-collections
mhornbacher Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,10 @@
"postcss": "^8.4.24",
"postcss-custom-properties": "^13.2.0",
"postcss-import": "^15.1.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-frame-component": "^5.0.0",
"react-test-renderer": "^19.1.0",
"react-test-renderer": "^19.2.0",
"recast": "^0.23",
"recursive-readdir": "^2.2.2",
"regenerator-runtime": "0.13.3",
Expand Down
18 changes: 18 additions & 0 deletions packages/@react-aria/collections/src/CollectionBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {forwardRefType, Key, Node} from '@react-types/shared';
import {Hidden} from './Hidden';
import React, {createContext, ForwardedRef, forwardRef, JSX, ReactElement, ReactNode, useCallback, useContext, useMemo, useRef, useState} from 'react';
import {useIsSSR} from '@react-aria/ssr';
import {useLayoutEffect} from '@react-aria/utils';
import {useSyncExternalStore as useSyncExternalStoreShim} from 'use-sync-external-store/shim/index.js';

const ShallowRenderContext = createContext(false);
Expand Down Expand Up @@ -113,6 +114,23 @@ function useCollectionDocument<T extends object, C extends BaseCollection<T>>(cr
return document.getCollection();
}, [document]);
let collection = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);

// React 19.2 introduced activities, which when started as hidden on the dom can play funky with the collection
// To avoid this we force a one time re-render if the collection fails to populate
const [, forceStoreRerender] = useState(0); // simple state to force a re-render
useLayoutEffect(function forceRefreshCollection() {
// Do not do anything if the collection has been pre-populated successfully
if (collection.size > 0) {
return;
}
// Re-create the collection
const nextValue = getSnapshot();
if (nextValue.size > 0) {
forceStoreRerender(c => c + 1);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [collection.size === 0]); // don't run effect once we have a populated collection

return {collection, document};
}

Expand Down
4 changes: 2 additions & 2 deletions packages/dev/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"highlight.js": "9.18.1",
"markdown-to-jsx": "^6.11.0",
"quicklink": "^2.3.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-lowlight": "^2.0.0"
},
"peerDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/dev/s2-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@
"json5": "^2.2.3",
"lz-string": "^1.5.0",
"markdown-to-jsx": "^6.11.0",
"react": "^19",
"react": "^19.2.0",
"react-aria": "^3.40.0",
"react-aria-components": "^1.7.1",
"react-dom": "^19",
"react-dom": "^19.2.0",
"react-stately": "^3.38.0",
"remark-mdx": "^3.1.0",
"remark-parse": "^11.0.0",
Expand Down
36 changes: 36 additions & 0 deletions packages/react-aria-components/test/ListBox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1861,4 +1861,40 @@ describe('ListBox', () => {
expect(onClick).toHaveBeenCalledTimes(1);
});
});

if (React.version.startsWith('19')) {
it('supports Activity', async () => {
function ActivityListBox() {
let [mode, setMode] = React.useState('hidden');

return (
<>
<Button onPress={() => setMode(mode === 'hidden' ? 'visible' : 'hidden')}>
Set {mode === 'hidden' ? 'visible' : 'hidden'}
</Button>

<React.Activity mode={mode}>
<p>List should be visible</p>

<ListBox aria-label="Activity Listbox">
<ListBoxItem>Item 1</ListBoxItem>
<ListBoxItem>Item 2</ListBoxItem>
<ListBoxItem>Item 3</ListBoxItem>
<ListBoxItem>Item 4</ListBoxItem>
<ListBoxItem>Item 5</ListBoxItem>
</ListBox>
</React.Activity>
</>
);
}

let {getAllByRole, getByRole, queryAllByRole} = render(<ActivityListBox />);
let button = getByRole('button');
expect(queryAllByRole('option')).toHaveLength(0);
await user.click(button);
expect(getAllByRole('option')).toHaveLength(5);
await user.click(button);
expect(queryAllByRole('option')).toHaveLength(0);
});
}
});
38 changes: 38 additions & 0 deletions packages/react-aria-components/test/Tabs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -641,4 +641,42 @@ describe('Tabs', () => {
expect(onClick).toHaveBeenCalledTimes(1);
});
});

if (React.version.startsWith('19')) {
it('supports Activity', async () => {
function ActivityTabs() {
let [mode, setMode] = React.useState('hidden');

return (
<>
<Button onPress={() => setMode(mode === 'hidden' ? 'visible' : 'hidden')}>
Set {mode === 'hidden' ? 'visible' : 'hidden'}
</Button>
<React.Activity mode={mode}>
<Tabs>
<TabList aria-label="Test">
<Tab id="a">A</Tab>
<Tab id="b">B</Tab>
<TooltipTrigger>
<Tab id="c">C</Tab>
<Tooltip>Test</Tooltip>
</TooltipTrigger>
</TabList>
<TabPanel id="a">A</TabPanel>
<TabPanel id="b">B</TabPanel>
<TabPanel id="c">C</TabPanel>
</Tabs>
</React.Activity>
</>
);
}

let {getByRole, getAllByRole, queryAllByRole} = render(<ActivityTabs />);

let button = getByRole('button');
expect(queryAllByRole('tab')).toHaveLength(0);
await user.click(button);
expect(getAllByRole('tab')).toHaveLength(3);
});
}
});
8 changes: 4 additions & 4 deletions starters/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@
"clsx": "^2.1.1",
"lightningcss-loader": "^2.1.0",
"lucide-react": "^0.517.0",
"react": "^19.1.0",
"react": "^19.2.0",
"react-aria-components": "^1.14.0",
"react-dom": "^19.1.0",
"react-dom": "^19.2.0",
"storybook": "^8.6.14",
"storybook-dark-mode": "^4.0.2",
"typescript": "^5.8.2"
},
"resolutions": {
"react": "19.1.0",
"react-dom": "19.1.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"@types/mime": "3.0.4",
"jackspeak": "2.1.1"
}
Expand Down
48 changes: 24 additions & 24 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6831,8 +6831,8 @@ __metadata:
highlight.js: "npm:9.18.1"
markdown-to-jsx: "npm:^6.11.0"
quicklink: "npm:^2.3.0"
react: "npm:^19.1.0"
react-dom: "npm:^19.1.0"
react: "npm:^19.2.0"
react-dom: "npm:^19.2.0"
react-lowlight: "npm:^2.0.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
Expand Down Expand Up @@ -7383,10 +7383,10 @@ __metadata:
lz-string: "npm:^1.5.0"
markdown-to-jsx: "npm:^6.11.0"
playwright: "npm:^1.57.0"
react: "npm:^19"
react: "npm:^19.2.0"
react-aria: "npm:^3.40.0"
react-aria-components: "npm:^1.7.1"
react-dom: "npm:^19"
react-dom: "npm:^19.2.0"
react-stately: "npm:^3.38.0"
remark-mdx: "npm:^3.1.0"
remark-parse: "npm:^11.0.0"
Expand Down Expand Up @@ -24695,14 +24695,14 @@ __metadata:
languageName: node
linkType: hard

"react-dom@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react-dom@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0, react-dom@npm:^19, react-dom@npm:^19.1.0":
version: 19.1.0
resolution: "react-dom@npm:19.1.0"
"react-dom@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react-dom@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0, react-dom@npm:^19.2.0":
version: 19.2.1
resolution: "react-dom@npm:19.2.1"
dependencies:
scheduler: "npm:^0.26.0"
scheduler: "npm:^0.27.0"
peerDependencies:
react: ^19.1.0
checksum: 10c0/3e26e89bb6c67c9a6aa86cb888c7a7f8258f2e347a6d2a15299c17eb16e04c19194e3452bc3255bd34000a61e45e2cb51e46292392340432f133e5a5d2dfb5fc
react: ^19.2.1
checksum: 10c0/e56b6b3d72314df580ca800b70a69a21c6372703c8f45d9b5451ca6519faefb2496d76ffa9c5adb94136d2bbf2fd303d0dfc208a2cd77ede3132877471af9470
languageName: node
linkType: hard

Expand Down Expand Up @@ -24887,10 +24887,10 @@ __metadata:
postcss: "npm:^8.4.24"
postcss-custom-properties: "npm:^13.2.0"
postcss-import: "npm:^15.1.0"
react: "npm:^19.1.0"
react-dom: "npm:^19.1.0"
react: "npm:^19.2.0"
react-dom: "npm:^19.2.0"
react-frame-component: "npm:^5.0.0"
react-test-renderer: "npm:^19.1.0"
react-test-renderer: "npm:^19.2.0"
recast: "npm:^0.23"
recursive-readdir: "npm:^2.2.2"
regenerator-runtime: "npm:0.13.3"
Expand Down Expand Up @@ -24976,17 +24976,10 @@ __metadata:
languageName: node
linkType: hard

"react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react@npm:^19, react@npm:^19.1.0":
version: 19.1.0
resolution: "react@npm:19.1.0"
checksum: 10c0/530fb9a62237d54137a13d2cfb67a7db6a2156faed43eecc423f4713d9b20c6f2728b026b45e28fcd72e8eadb9e9ed4b089e99f5e295d2f0ad3134251bdd3698
languageName: node
linkType: hard

"react@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0":
version: 19.1.1
resolution: "react@npm:19.1.1"
checksum: 10c0/8c9769a2dfd02e603af6445058325e6c8a24b47b185d0e461f66a6454765ddcaecb3f0a90184836c68bb509f3c38248359edbc42f0d07c23eb500a5c30c87b4e
"react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0, react@npm:^19.2.0":
version: 19.2.1
resolution: "react@npm:19.2.1"
checksum: 10c0/2b5eaf407abb3db84090434c20d6c5a8e447ab7abcd8fe9eaf1ddc299babcf31284ee9db7ea5671d21c85ac5298bd632fa1a7da1ed78d5b368a537f5e1cd5d62
languageName: node
linkType: hard

Expand Down Expand Up @@ -25971,6 +25964,13 @@ __metadata:
languageName: node
linkType: hard

"scheduler@npm:^0.27.0":
version: 0.27.0
resolution: "scheduler@npm:0.27.0"
checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452
languageName: node
linkType: hard

"section-matter@npm:^1.0.0":
version: 1.0.0
resolution: "section-matter@npm:1.0.0"
Expand Down