React raises this error whenever the bundle that ends up in the browser is still running a build that predates React 18 or resolves to a different runtime than the one supplying <code>useId</code>. The hook was introduced in React 18 for stable ID generation, so any version mismatch, duplicate React resolution, or legacy CDN script that delivers React 17 causes the runtime to throw before your components render. This guide walks through the typical traps (duplicate versions, bundler aliases, cached scripts) and the steps to lock everything on React 18+ so the hook is actually available.
React throws this error when the copy of React that the browser actually executes does not export the <code>useId</code> hook. <code>useId</code> was introduced in React 18 to generate deterministic IDs that hydrate safely across server and client renders. Older React builds (React 17 and earlier) simply do not have this hook. The message is therefore a symptom of a version mismatch, duplicate React packages, or a bundler/resolution configuration that still loads a legacy entry point. Once the runtime bundle resolves to React 18 or newer, the hook exists and the error disappears. Ensuring both <code>react</code> and <code>react-dom</code> are aligned and that only one copy of the package ships is the key to fixing this.
Run npm ls react react-dom (or pnpm ls/yarn why) to see which versions are currently resolved. If either dependency shows a version below 18.0.0, upgrade both packages together:
npm install react@18 react-dom@18or lock to a specific patch:
npm install [email protected] [email protected]After installing, clear module caches (rm -rf node_modules package-lock.json pnpm-lock.yaml) and reinstall to ensure the new versions are the only ones on disk.
Monorepo setups or transitive dependencies often install multiple React versions. Use dedupe commands (e.g., npm dedupe, pnpm dedupe, yarn install --check-files) and re-run npm ls react until only the React 18 copy remains. If you are using Yarn or pnpm workspaces, add a resolutions (Yarn) or packageExtensions (pnpm) entry so every package resolves to the same React version.
// package.json (Yarn)
"resolutions": {
"react": "18.2.0",
"react-dom": "18.2.0"
}Then rerun your installer and rebuild.
Ask Node which file is being loaded in the build:
node -p "require.resolve('react')"
node -p "require.resolve('react-dom')"They should point into node_modules/react and not into any vendor/CDN shim. If you see absolute paths pointing to a legacy folder, adjust your bundler’s resolve.alias so react and react-dom both resolve to your root node_modules. Vite typically resolves correctly, but Webpack projects may have leftover aliases from older migrations that still point to older builds.
After touching dependencies or resolution overrides, remove build artifacts so the next compile can pull fresh modules:
rm -rf node_modules package-lock.json pnpm-lock.yaml
npm install
rm -rf dist .next build
npm run buildAlso clear any service worker or CDN caches that might still serve the old bundle, and hard-refresh the browser (Ctrl+Shift+R / Cmd+Shift+R) when testing.
Search your HTML template for <script ... react tags. Remove any statements that load React 17 from a CDN, because they override your bundled React 18 with an older runtime. If you absolutely need a CDN build, make sure it is React 18:
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>Using npm-managed packages is still preferred, but if you reference CDN scripts, always point them to a matching React 18 build.
Frameworks that ship their own React version also need to be on a React 18 release. For Create React App, install react-scripts 5.0.0 or later:
npm install react-scripts@5In Next.js, run npm install next@latest (Next 12.1+ automatically ships React 18) and use the App Router or Client Components for parts that call hooks like useId. Make sure any custom entry points use ReactDOM.createRoot instead of the legacy render so the client bundle uses the modern renderer.
<strong>Why useId exists:</strong> React 18 introduced useId to emit deterministic IDs that survive SSR and hydration, avoiding duplicated IDs or mismatched aria attributes. The hook internally generates a prefix and counter that depend on the renderer, so the client and server must run the same React build. If useId throws, it usually means the client is still executing a build that predates React 18 or is not the build bundled by your module system (e.g., global scripts, CDN caches, or duplicate installs).<br/><br/><strong>Hydration alignment:</strong> When using server rendering we adapt a special _document or template. The React renderer uses underscores in new SSR builds, so mismatched versions can also show useId errors even when the source code has the hook. For consistent hydration, keep both server and client bundles on the same React 18+ release.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite