All guidesMore providers: databases, auth, hosting, email and payments
On this page

React Router error

React

useNavigate() may be used only in the context of a <Router> component.

By Ohad Krispin, founder of VibeRaven · LinkedIn

"useNavigate() may be used only in the context of a <Router> component." is thrown by React Router when a component calls the hook and no router sits above it in the tree. useLocation, useHref, useMatch, useRoutes and <Navigate> throw the same sentence with their own name. The causes are a component rendered next to the router instead of inside it, a test or library that mounts it alone, and two copies of react-router, where the hook comes from one copy and the router from another; a comment in the library's source says the error is probably that. In a react-router 6.30.6 production build the message is empty, and with no error boundary the deployed page is blank. Reproduced with react-router 6.30.6, 7.18.5 and 8.5.1 on 2026-10-31.

Why is the useNavigate error message empty in production?

Version 6 builds the throw as UNSAFE_invariant(false) with no text outside development (index.js in react-router 6.30.6). A render in a production build gave an Error with an empty message, while the 7.18.5 and 8.5.1 builds kept the text. To read it, run the screen in development or render the component in a test.

react-router 6.30.6, NODE_ENV=production:  ""
react-router 6.30.6, NODE_ENV=development: "useNavigate() may be used only in the context of a <Router> component."
react-router 7.18.5, NODE_ENV=production:  "useNavigate() may be used only in the context of a <Router> component."

How do I find two copies of react-router?

The router and the hooks must come from the same installed copy, because each copy has its own context. In a run, a MemoryRouter from react-router 8.5.1 with a hook from react-router 6.30.6 below it threw the error. The npm registry lists react-router-dom 6.30.6 as depending on react-router 6.30.6, so a package.json that has react-router 7 next to react-router-dom 6 installs both. Import every router API from one package. In a monorepo or with a linked package, Vite's resolve.dedupe option forces the listed dependencies to resolve to the same copy.

npm ls react-router react-router-dom

// vite.config.ts
resolve: { dedupe: ['react', 'react-dom', 'react-router'] },

Why does useLoaderData say it must be used within a data router?

It is the same family of error with a different cause. Calling useLoaderData under a plain MemoryRouter threw "useLoaderData must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router." The library builds that message from the hook name. The useLoaderData docs mark it Available with Data and Not available with Declarative, and the modes page pairs BrowserRouter with Declarative mode and createBrowserRouter with RouterProvider for Data mode. Move to a data router, or stop using the hook.

const router = createBrowserRouter([
  { path: '/', Component: Root, loader: rootLoader },
]);

createRoot(document.getElementById('root')).render(
  <RouterProvider router={router} />
);

Checklist

How to fix it, then check the fix

  1. 01
    Find where the router is created and which files call the hooks:
    grep -rnE "BrowserRouter|createBrowserRouter|RouterProvider|MemoryRouter" src
    grep -rlE "useNavigate|useLocation|useHref|<Navigate" src
  2. 02
    Move every component that calls a router hook inside the router. A toolbar or toast rendered beside the router in main.tsx has no router above it:
    createRoot(root).render(
      <BrowserRouter>
        <Toolbar />
        <App />
      </BrowserRouter>
    );
  3. 03
    Check for a second copy of react-router, remove the extra package, and import from one package everywhere:
    npm ls react-router react-router-dom
  4. 04In a test, render the component inside a MemoryRouter.
  5. 05If the deployed page is blank and the console has no message, reproduce the screen with npm run dev: version 6 production builds carry no text for this error.
  6. 06Put an error boundary above the routes, then open the production build and click a link that calls navigate: the URL changes and the console is clean.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Does a BrowserRouter in App.tsx cover a component in main.tsx?

No. Only components rendered below the router read its context. The useInRouterContext docs say the hook returns true if the component is a descendant of a Router, so it can show where the router ends.

Is the message the same for useRoutes and Navigate?

Yes, with the name changed. The react-router 8.5.1 build contains the same sentence for useHref, useLocation, useMatch, useNavigate and useRoutes, and for <Navigate>.