diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md
index b8eab99..25ae52a 100644
--- a/packages/@headlessui-react/CHANGELOG.md
+++ b/packages/@headlessui-react/CHANGELOG.md
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Merge incoming `style` prop on `ComboboxOptions`, `ListboxOptions`, `MenuItems`, and `PopoverPanel` components ([#3250](https://github.com/tailwindlabs/headlessui/pull/3250))
- Prevent focus on `` when it is `disabled` ([#3251](https://github.com/tailwindlabs/headlessui/pull/3251))
- Fix visual jitter in `Combobox` component when using native scrollbar ([#3190](https://github.com/tailwindlabs/headlessui/pull/3190))
+- Use `useId` instead of React internals (for React 19 compatibility) ([#3254](https://github.com/tailwindlabs/headlessui/pull/3254))
## [2.0.4] - 2024-05-25
diff --git a/packages/@headlessui-react/src/utils/stable-collection.tsx b/packages/@headlessui-react/src/utils/stable-collection.tsx
index e9feacf..e2b7c10 100644
--- a/packages/@headlessui-react/src/utils/stable-collection.tsx
+++ b/packages/@headlessui-react/src/utils/stable-collection.tsx
@@ -18,6 +18,9 @@ function createCollection() {
}
let renders = list.get(key) ?? 0
+ // FIXME: This is a side-effect during render. `release` is only called in
+ // an effect cleanup so we may never release if we had to render multiple
+ // times before commit e.g. when a sibling suspends.
list.set(key, renders + 1)
let index = Array.from(list.keys()).indexOf(key)
@@ -49,33 +52,8 @@ export function useStableCollectionIndex(group: string) {
let collection = React.useContext(StableCollectionContext)
if (!collection) throw new Error('You must wrap your component in a ')
- let key = useStableCollectionKey()
+ let key = React.useId()
let [idx, cleanupIdx] = collection.current.get(group, key)
React.useEffect(() => cleanupIdx, [])
return idx
}
-
-/**
- * Return a stable key based on the position of this node.
- *
- * @returns {symbol | string}
- */
-function useStableCollectionKey() {
- let owner =
- // @ts-ignore
- React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?.ReactCurrentOwner?.current ?? null
-
- // ssr: dev/prod
- // client: prod
- if (!owner) return Symbol()
-
- // client: dev
- let indexes = []
- let fiber = owner
- while (fiber) {
- indexes.push(fiber.index)
- fiber = fiber.return
- }
-
- return '$.' + indexes.join('.')
-}