tendinitis
tendinitis

Reputation: 1007

Error: useHref() may be used only in the context of a <Router> component. It works when I directly put the url as localhost:3000/experiences

I have a navbar that is rendered in every route while the route changes on click.

./components/navbar.jsx

import React, { Component } from 'react';
import '../App.css';
import { Link } from 'react-router-dom';



class Navbar extends Component {
    constructor(props) {
        super(props);
        this.state = {};
    }
    render() {
        return (
            <div id = 'navbar'>

                <div className='name-head'>
                    My Name
                </div>
            
            
                <div id = 'nav-links-container'>
                    
                    <Link to='/experiences'>
                        <div className = 'nav-links'>
                            Experiences
                        </div>
                    </Link>

                    <div className = 'nav-links'>
                        Projects
                    </div>

                    <div className = 'nav-links'>
                        Skills
                    </div>

                    <div className = 'nav-links'>
                        Resume
                    </div>

                </div>
                
            </div>
        );
    }
}

export default Navbar;

./components/experiences.jsx

import React, { Component } from 'react';


class Experiences extends Component {
    
    render() { 
        return (
            <div>
                <h1>hi</h1>
            </div>
        );
    }
}
 
export default Experiences;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
import Navbar from './components/Navbar';
import Home from './components/Home';
import Experiences from './components/experience';

import {
  BrowserRouter as Router, 
  Routes, 
  Route
} from 'react-router-dom';



ReactDOM.render(

  <React.StrictMode>

    <Navbar />

    <Router>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/experiences" element={<Experiences />} />
      </Routes>

    </Router>

  </React.StrictMode>,

  document.getElementById('root')
);


reportWebVitals();

The error doesn't come when I remove the <Link> from the experiences tag in navbar. There is a similar question posted here: Error: useHref() may be used only in the context of a <Router> component but doesn't help.

I'm using react router v6

Upvotes: 98

Views: 134329

Answers (12)

Daniel
Daniel

Reputation: 15413

@Yilmaz gives an excellent answer about this also occurring in a testing environment and I can confirm that is the case having experienced before and would like to offer a condensed answer of all the good ones given here.

Yes Link component will try to reach up and find the context object at the top of the component hierarchy and yes indeed this occurs in a testing environment as well.

Whenever we render one of these different react router component so something like Link or some other, even though we are in a test environment and we don’t really care about navigation at all, we still have to provide one of these three options available to us, we can try to wrap our component that we are trying to test in a:

  1. BrowserRouter
  2. HashRouter
  3. MemoryRouter

How do you know which one to use? What is the difference?

Well, a BrowserRouter stores current URL in the address bar, a HashRouter stores current URL in the # part of the address bar and a MemoryRouter stores current URL in memory.

Like @Yilmaz I would also recommend going with a MemoryRouter in a testing environment, although there are some tests where BrowserRouter might be best to use long term.

Upvotes: 0

Yilmaz
Yilmaz

Reputation: 49182

This error happens because Link component needs to reach out to react-router context object. Your Navbar component is using Link component but Navbar is not wrapped by router context.

A similar error happens in a testing environment. If you have a component that uses Link component and if you render this component in a testing environment, you will get the same error because Link component needs to access a router context. In the case of test environment:

import { render } from "@testing-library/react";

// this will throw same error
test("testing", () => {
  render(<ComponentRendersLink/>); 
});

Solution in this case to wrap it with a router

// there are more router options
import { MemoryRouter } from "react-router-dom";

render(
    <MemoryRouter>
      <ComponentRendersLink />
    </MemoryRouter>
  );

Upvotes: 2

Drew Reese
Drew Reese

Reputation: 202618

Issue

You are rendering the navbar outside the routing context. The Router isn't aware of what routes the links are attempting to link to that it is managing. The reason routing works when directly navigating to "/experiences" is because the Router is aware of the URL when the app mounts.

<Navbar /> // <-- outside router!!

<Router>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/experiences" element={<Experiences />} />
  </Routes>
</Router>

Solution

Move it inside the routing context so the Router is aware and can manage routing correctly.

<Router>
  <Navbar />
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/experiences" element={<Experiences />} />
  </Routes>
</Router>

[email protected] Data APIs

If you are using the new Data routers you can hit this issue if you attempt to render a header/navbar outside the RouterProvider component. For this you can create a layout route that is part of the routing configuration passed to createBrowserRouter (and other variants).

Example:

const AppLayout = () => (
  <>
    <Navbar />
    <Outlet />
  </>
);

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route element={<AppLayout />}>
      <Route path="/" element={<Home />} />
      <Route path="/experiences" element={<Experiences />} />
    </Route>
  )
);

...

<RouterProvider router={router} />

Upvotes: 169

KARTHIKEYAN.A
KARTHIKEYAN.A

Reputation: 20078

In react-router-dom:6.x and react:18.x, we should use the Router in the following way to resolve the issue:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { App }  from './App.js';

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <React.StrictMode>
    <Router>  // Router
      <App /> // Application
    </Router>
  </React.StrictMode>
); 

Upvotes: 10

Greg Titan
Greg Titan

Reputation: 181

If you are still having problem with this one, it is because react-router-dom relies on React context to work when you try to unit-test it. This makes <Link /> or <Route /> obsolete.
Try reading the react-router-dom documentation.

Instead of using

render(<Example />)

that have either or inside it, you can try

render(<MemoryRouter>
    <Example />
</MemoryRouter>)

Hope this solves your problem

Upvotes: 18

Adebisi
Adebisi

Reputation: 79

Your links just needs to be within a BrowserRouter component since you use v6 After importing

<BrowserRouter>
    <Link to='page'>
</BrowserRouter>

Upvotes: 1

jckmlczk
jckmlczk

Reputation: 622

This is very much an edge case, but in my case it turned out a lib I develop had react-router-dom: 6.0.2 installed and project that uses it v6.3.0

Upvotes: 0

Nurul Islam
Nurul Islam

Reputation: 53

Install in your project React Router v6.

npm install react-router-dom@6 

Then use BrowserRouter in your index.js file, below like this:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
<React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

Upvotes: 5

Al Mamun Khan
Al Mamun Khan

Reputation: 737

React Router v6 this problem common one, you can simply replace "index.js" code. you will got solution-

import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

const container = document.getElementById('root');
const root = createRoot(container);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

Upvotes: 2

Ravindu Sathsara
Ravindu Sathsara

Reputation: 135

Wrap navbar with BrowserRouter

import { BrowserRouter, Routes, Route } from "react-router-dom";
<BrowserRouter>
    <AppNavBar />
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/wall" element={<WallPost />} />
    </Routes>
  </BrowserRouter>

Upvotes: 5

chaffa
chaffa

Reputation: 81

Links are inside Navbar & Navbar is outside of Router => links are outside of Router => Router will not manage Links

Solution

Move the Navbar into Router section. Example:

<Router>
  <Navbar /> // <===========
  <Routes>
    <Route />
    <Route />
  </Routes>
</Router>

Upvotes: 8

Alejandro De Castro
Alejandro De Castro

Reputation: 2247

in React Route v6 you can solve this giving the route context to your entire App with <BrowserRouter>

This is an complete example of index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { App }  from './components/App/App.jsx';


ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>  //that is the key
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

Upvotes: 36

Related Questions