diff --git a/.changeset/grumpy-news-happen.md b/.changeset/grumpy-news-happen.md
new file mode 100644
index 000000000000..7172ba997e30
--- /dev/null
+++ b/.changeset/grumpy-news-happen.md
@@ -0,0 +1,5 @@
+---
+'@ag.ds-next/react': minor
+---
+
+Removed dependency to Reach UI as it is no longer being maintained. The logic from `@reach/auto-id` has been forked and copied into the repo.
\ No newline at end of file
diff --git a/packages/react/package.json b/packages/react/package.json
index 566046b79847..9cbcd2fd8e78 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -7,7 +7,6 @@
"dependencies": {
"@babel/runtime": "^7.4.5",
"@popperjs/core": "^2.11.4",
- "@reach/auto-id": "^0.18.0",
"@react-spring/web": "^9.4.5",
"date-fns": "^2.28.0",
"downshift": "^6.1.7",
diff --git a/packages/react/src/core/utils/useId.ts b/packages/react/src/core/utils/useId.ts
deleted file mode 100644
index d309d1a06964..000000000000
--- a/packages/react/src/core/utils/useId.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { useId } from '@reach/auto-id';
diff --git a/packages/react/src/core/utils/useId/canUseDom.ts b/packages/react/src/core/utils/useId/canUseDom.ts
new file mode 100644
index 000000000000..9f69e308abc8
--- /dev/null
+++ b/packages/react/src/core/utils/useId/canUseDom.ts
@@ -0,0 +1,9 @@
+// The contents of this file has been copied from https://github.com/reach/reach-ui/blob/dev/packages/utils/src/can-use-dom.ts
+
+export function canUseDOM() {
+ return !!(
+ typeof window !== 'undefined' &&
+ window.document &&
+ window.document.createElement
+ );
+}
diff --git a/packages/react/src/core/utils/useId/index.ts b/packages/react/src/core/utils/useId/index.ts
new file mode 100644
index 000000000000..d9387a007fe9
--- /dev/null
+++ b/packages/react/src/core/utils/useId/index.ts
@@ -0,0 +1 @@
+export { useId } from './useId';
diff --git a/packages/react/src/core/utils/useId/useId.test.tsx b/packages/react/src/core/utils/useId/useId.test.tsx
new file mode 100644
index 000000000000..449419bfa161
--- /dev/null
+++ b/packages/react/src/core/utils/useId/useId.test.tsx
@@ -0,0 +1,35 @@
+// The contents of this file has been copied from https://github.com/reach/reach-ui/blob/dev/packages/auto-id/__tests__/auto-id.test.tsx
+
+import { render, cleanup } from '../../../../../../test-utils';
+import { useId } from './useId';
+
+afterEach(cleanup);
+
+describe('useId', () => {
+ it('should generate a unique ID value', () => {
+ function Comp() {
+ const justNull = null;
+ const randId = useId(justNull);
+ const randId2 = useId();
+ return (
+
+ );
+ }
+ const { getByText } = render();
+ const id1 = getByText('Wow').id;
+ const id2 = getByText('Ok').id;
+ expect(id2).not.toEqual(id1);
+ });
+
+ it('uses a fallback ID', () => {
+ function Comp() {
+ const newId = useId('awesome');
+ return Ok
;
+ }
+ const { getByText } = render();
+ expect(getByText('Ok').id).toEqual('awesome');
+ });
+});
diff --git a/packages/react/src/core/utils/useId/useId.ts b/packages/react/src/core/utils/useId/useId.ts
new file mode 100644
index 000000000000..b1ef0ede267f
--- /dev/null
+++ b/packages/react/src/core/utils/useId/useId.ts
@@ -0,0 +1,139 @@
+/**
+ * This file use to just be a simple re-export of `useId` from @reach/auto-id
+ * We can not just use `useId` from React as we need to provide support for React 16, 17 and 18
+ *
+ * As Reach is no longer being maintained and does not support React 18, the contents of this file have been copied from:
+ * https://github.com/reach/reach-ui/blob/dev/packages/auto-id/src/reach-auto-id.ts
+ *
+ * See Github issue: https://github.com/reach/reach-ui/issues/972
+ */
+
+/**
+ * Welcome to @reach/auto-id!
+ * Let's see if we can make sense of why this hook exists and its
+ * implementation.
+ *
+ * Some background:
+ * 1. Accessibility APIs rely heavily on element IDs
+ * 2. Requiring developers to put IDs on every element in Reach UI is both
+ * cumbersome and error-prone
+ * 3. With a component model, we can generate IDs for them!
+ *
+ * Solution 1: Generate random IDs.
+ *
+ * This works great as long as you don't server render your app. When React (in
+ * the client) tries to reuse the markup from the server, the IDs won't match
+ * and React will then recreate the entire DOM tree.
+ *
+ * Solution 2: Increment an integer
+ *
+ * This sounds great. Since we're rendering the exact same tree on the server
+ * and client, we can increment a counter and get a deterministic result between
+ * client and server. Also, JS integers can go up to nine-quadrillion. I'm
+ * pretty sure the tab will be closed before an app never needs
+ * 10 quadrillion IDs!
+ *
+ * Problem solved, right?
+ *
+ * Ah, but there's a catch! React's concurrent rendering makes this approach
+ * non-deterministic. While the client and server will end up with the same
+ * elements in the end, depending on suspense boundaries (and possibly some user
+ * input during the initial render) the incrementing integers won't always match
+ * up.
+ *
+ * Solution 3: Don't use IDs at all on the server; patch after first render.
+ *
+ * What we've done here is solution 2 with some tricks. With this approach, the
+ * ID returned is an empty string on the first render. This way the server and
+ * client have the same markup no matter how wild the concurrent rendering may
+ * have gotten.
+ *
+ * After the render, we patch up the components with an incremented ID. This
+ * causes a double render on any components with `useId`. Shouldn't be a problem
+ * since the components using this hook should be small, and we're only updating
+ * the ID attribute on the DOM, nothing big is happening.
+ *
+ * It doesn't have to be an incremented number, though--we could do generate
+ * random strings instead, but incrementing a number is probably the cheapest
+ * thing we can do.
+ *
+ * Additionally, we only do this patchup on the very first client render ever.
+ * Any calls to `useId` that happen dynamically in the client will be
+ * populated immediately with a value. So, we only get the double render after
+ * server hydration and never again, SO BACK OFF ALRIGHT?
+ */
+
+/* eslint-disable react-hooks/rules-of-hooks */
+
+import * as React from 'react';
+import { useIsomorphicLayoutEffect as useLayoutEffect } from './useIsomorphicLayoutEffect';
+
+let serverHandoffComplete = false;
+let id = 0;
+function genId() {
+ return ++id;
+}
+
+// Workaround for https://github.com/webpack/webpack/issues/14814
+// https://github.com/eps1lon/material-ui/blob/8d5f135b4d7a58253a99ab56dce4ac8de61f5dc1/packages/mui-utils/src/useId.ts#L21
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const maybeReactUseId: undefined | (() => string) = (React as any)[
+ 'useId'.toString()
+];
+
+/**
+ * useId
+ *
+ * Autogenerate IDs to facilitate WAI-ARIA and server rendering.
+ *
+ * Note: The returned ID will initially be `null` and will update after a
+ * component mounts. Users may need to supply their own ID if they need
+ * consistent values for SSR.
+ *
+ * @see Docs https://reach.tech/auto-id
+ */
+function useId(idFromProps: string): string;
+function useId(idFromProps: number): number;
+function useId(idFromProps: string | number): string | number;
+function useId(idFromProps: string | undefined | null): string | undefined;
+function useId(idFromProps: number | undefined | null): number | undefined;
+function useId(
+ idFromProps: string | number | undefined | null
+): string | number | undefined;
+function useId(): string | undefined;
+
+function useId(providedId?: number | string | undefined | null) {
+ if (maybeReactUseId !== undefined) {
+ const generatedId = maybeReactUseId();
+ return providedId ?? generatedId;
+ }
+
+ // If this instance isn't part of the initial render, we don't have to do the
+ // double render/patch-up dance. We can just generate the ID and return it.
+ const initialId = providedId ?? (serverHandoffComplete ? genId() : null);
+ const [id, setId] = React.useState(initialId);
+
+ useLayoutEffect(() => {
+ if (id === null) {
+ // Patch the ID after render. We do this in `useLayoutEffect` to avoid any
+ // rendering flicker, though it'll make the first render slower (unlikely
+ // to matter, but you're welcome to measure your app and let us know if
+ // it's a problem).
+ setId(genId());
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ React.useEffect(() => {
+ if (serverHandoffComplete === false) {
+ // Flag all future uses of `useId` to skip the update dance. This is in
+ // `useEffect` because it goes after `useLayoutEffect`, ensuring we don't
+ // accidentally bail out of the patch-up dance prematurely.
+ serverHandoffComplete = true;
+ }
+ }, []);
+
+ return providedId ?? id ?? undefined;
+}
+
+export { useId };
diff --git a/packages/react/src/core/utils/useId/useIsomorphicLayoutEffect.ts b/packages/react/src/core/utils/useId/useIsomorphicLayoutEffect.ts
new file mode 100644
index 000000000000..400084db265b
--- /dev/null
+++ b/packages/react/src/core/utils/useId/useIsomorphicLayoutEffect.ts
@@ -0,0 +1,32 @@
+// The contents of this file has been copied from https://github.com/reach/reach-ui/blob/dev/packages/utils/src/use-isomorphic-layout-effect.ts
+
+import { useEffect, useLayoutEffect } from 'react';
+import { canUseDOM } from './canUseDom';
+
+/**
+ * React currently throws a warning when using useLayoutEffect on the server. To
+ * get around it, we can conditionally useEffect on the server (no-op) and
+ * useLayoutEffect in the browser. We occasionally need useLayoutEffect to
+ * ensure we don't get a render flash for certain operations, but we may also
+ * need affected components to render on the server. One example is when setting
+ * a component's descendants to retrieve their index values.
+ *
+ * Important to note that using this hook as an escape hatch will break the
+ * eslint dependency warnings unless you rename the import to `useLayoutEffect`.
+ * Use sparingly only when the effect won't effect the rendered HTML to avoid
+ * any server/client mismatch.
+ *
+ * If a useLayoutEffect is needed and the result would create a mismatch, it's
+ * likely that the component in question shouldn't be rendered on the server at
+ * all, so a better approach would be to lazily render those in a parent
+ * component after client-side hydration.
+ *
+ * https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
+ * https://github.com/reduxjs/react-redux/blob/master/src/utils/useIsomorphicLayoutEffect.js
+ *
+ * @param effect
+ * @param deps
+ */
+export const useIsomorphicLayoutEffect = canUseDOM()
+ ? useLayoutEffect
+ : useEffect;
diff --git a/yarn.lock b/yarn.lock
index f64447af6b5c..24ec50c2e861 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2253,18 +2253,6 @@
resolved "https://registry.yarnpkg.com/@preconstruct/next/-/next-4.0.0.tgz#4d9c64ed68bb7cdc72d35d79d1dbe26ba2cae61e"
integrity sha512-vSrc8wFQgBErU7dKTKSQtr/DLWPHcN9jMoiWOAQodB1+B4Kpqqry6QhGYoRm0DQU5gNL+Rcp+Xb350O1E/gjsg==
-"@reach/auto-id@^0.18.0":
- version "0.18.0"
- resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.18.0.tgz#4b97085cd1cf1360a9bedc6e9c78e97824014f0d"
- integrity sha512-XwY1IwhM7mkHZFghhjiqjQ6dstbOdpbFLdggeke75u8/8icT8uEHLbovFUgzKjy9qPvYwZIB87rLiR8WdtOXCg==
- dependencies:
- "@reach/utils" "0.18.0"
-
-"@reach/utils@0.18.0":
- version "0.18.0"
- resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.18.0.tgz#4f3cebe093dd436eeaff633809bf0f68f4f9d2ee"
- integrity sha512-KdVMdpTgDyK8FzdKO9SCpiibuy/kbv3pwgfXshTI6tEcQT1OOwj7BAksnzGC0rPz0UholwC+AgkqEl3EJX3M1A==
-
"@react-spring/animated@~9.4.5":
version "9.4.5"
resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.4.5.tgz#dd9921c716a4f4a3ed29491e0c0c9f8ca0eb1a54"