81

I have a problem that I don't know how to solve, I get this error when running npm test

Invariant Violation: You should not use <Switch> outside a <Router>

What can the problem be and how can I solve it? The test I run is the standard app.test.js that comes with react

class App extends Component {
  render() {
    return (
      <div className = 'app'>
        <nav>
          <ul>
            <li><Link exact activeClassName="current" to='/'>Home</Link></li>
            <li><Link exact activeClassName="current" to='/TicTacToe'>TicTacToe</Link></li>
            <li><Link exact activeClassName="current" to='/NumGame'>Quick Maths</Link></li>
            <li><Link exact activeClassName="current" to='/HighScore'>Highscore</Link></li>
            <li><Link exact activeClassName="current" to='/Profile'>Profile</Link></li>
            <li><Link exact activeClassName="current" to='/Login'>Sign out</Link></li>
          </ul>
        </nav>
        <Switch>
          <Route exact path='/' component={Home}></Route>
          <Route path='/TicTacToe' component={TicTacToe}></Route>
          <Route path='/NumGame' component={NumberGame}></Route>
          <Route path='/HighScore' component={HighScore}></Route>
          <Route path='/Profile' component={Profile}></Route>
          <Route path='/Login' component={SignOut1}></Route>
        </Switch>
      </div>
    );
  }
};
Siya Mzam
  • 4,655
  • 1
  • 26
  • 44
Fille_M
  • 873
  • 1
  • 6
  • 7
  • 1
    Note that this error doesn't have anything to do with the fact you're running a unit test - you'd get it at runtime too. – Joe Clay May 29 '18 at 13:00

11 Answers11

170

The error is correct. You need to wrap the Switch with BrowserRouter or other alternatives like HashRouter, MemoryRouter. This is because BrowserRouter and alternatives are the common low-level interface for all router components and they make use of the HTML 5 history API, and you need this to navigate back and forth between your routes.

Try doing this rather

import { BrowserRouter, Switch, Route } from 'react-router-dom';

And then wrap everything like this

<BrowserRouter>
 <Switch>
  //your routes here
 </Switch>
</BrowserRouter>
Siya Mzam
  • 4,655
  • 1
  • 26
  • 44
15

The proper way to handle this, according to React Router devs, is to wrap your unit test in a Router. Using MemoryRouter is recommended in order to be able to reset the router between tests.

You can still do something like the following:

<BrowserRouter>
  <App />
</BrowserRouter>

Then in App:

<Switch>
  <Route />
  <Route />
</Switch>

Your unit tests for App would normally be something like:

const content = render(<App />); // Fails Unit test

Update the unit test to:

const content = render(<MemoryRouter><App /></MemoryRouter>); // Passes Unit test
nathanpdaniel
  • 151
  • 1
  • 4
5

Always put BrowserRouter in the navegations components, follow the example:

import React, { Component } from 'react'
import { render } from 'react-dom'
import { BrowserRouter, Route, NavLink, Switch } from 'react-router-dom'

var Componente1 = () => (<div>Componente 1</div>)
var Componente2 = () => (<div>Componente 2</div>)
var Componente3 = () =>  (<div>Componente 3</div>)

class Rotas extends Component {
    render() {

        return (
                <Switch>
                    <Route exact path='/' component={Componente1}></Route>
                    <Route exact path='/comp2' component={Componente2}></Route>
                    <Route exact path='/comp3' component={Componente3}></Route>
                </Switch>
        )
    }
}


class Navegacao extends Component {
    render() {
        return (

                <ul>
                    <li>
                        <NavLink to="/">Comp1</NavLink>
                    </li>
                    <li>
                        <NavLink exact  to="/comp2">Comp2</NavLink>
                    </li>
                    <li>
                        <NavLink exact to="/comp3">Comp3</NavLink>
                    </li>
                </ul>
        )
    }
}

class App extends Component {
    render() {
        return (
            <BrowserRouter>
                <div>
                    <Navegacao />
                    <Rotas />
                </div>
            </BrowserRouter>
        )
    }
}

render(<App/>, document.getElementById("root"))

Note: the BrowserRouter accept only one children element.

5

Make sure to have correct imports in all nested components. You might get that error if one of them imports Switch from react-router instead of react-router-dom. Keeping everything consistent with 'react-router-dom' (that reexports react-router components anyway). Checked with:

"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
Chris Hermut
  • 1,708
  • 3
  • 17
  • 32
1

The way I did was updating the dependencies with yarn add react-router-dom or npm install react-router-domand deleting the node_modules folder and running yarn or npm install again.

  • I had no to be found in my project, maybe was a call to google apis without proper auth the reason. Anyway, your solution upgraded react-router-dom from 5.2.0 to 5.3.0 and fixed this error, thanks! – pikiou Oct 20 '21 at 00:53
0

I was facing the same issue. It's resolved when I "import BrowserRouter from react-router-dom" and write code

<BrowserRouter>
 <Switch>
  //your routes here
 </Switch>
</BrowserRouter>
0

You can't use react-router 4.3 with react-router-dom 4.4 or vice versa. (Edit: writing it out like that: Why isn't that considered a breaking change?)

Make sure you will have same versions

atom217
  • 971
  • 1
  • 14
  • 26
0

You Should write your code like this

import {BrowserRouter, Switch, Router} from 'react-router-dom
 
 class App extends Component {
  render() {
    return (
      <div className = 'app'>
        <nav>
          <ul>
            <li><Link exact activeClassName="current" to='/'>Home</Link></li>
            <li><Link exact activeClassName="current" to='/TicTacToe'>TicTacToe</Link></li>
            <li><Link exact activeClassName="current" to='/NumGame'>Quick Maths</Link></li>
            <li><Link exact activeClassName="current" to='/HighScore'>Highscore</Link></li>
            <li><Link exact activeClassName="current" to='/Profile'>Profile</Link></li>
            <li><Link exact activeClassName="current" to='/Login'>Sign out</Link></li>
          </ul>
        </nav>

     <BrowserRouter>
        <Switch>
          <Route exact path='/' component={Home}></Route>
          <Route path='/TicTacToe' component={TicTacToe}></Route>
          <Route path='/NumGame' component={NumberGame}></Route>
          <Route path='/HighScore' component={HighScore}></Route>
          <Route path='/Profile' component={Profile}></Route>
          <Route path='/Login' component={SignOut1}></Route>
        </Switch>
      <BrowserRouter/>
      </div>
    );
  }
};
Rupam
  • 75
  • 1
  • 9
0
Error: Invariant failed: You should not use <Link>,<Switch>,<Route> outside a <Router>[enter image description here][1]

[Error: Invariant failed: You should not use <Link>,<Switch>,<Route> outside a <Router> ][1]

 1. Open Index.js or Index.jsx
 2. add-> import { BrowserRouter } from "react-router-dom";
 3.Rap <App /> in <BrowserRouter> and </BrowserRouter> 
should  look like :
ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById("root")
);
  • This question already contains multiple answers and an accepted answer. Can you explain (by editing your answer) where your answer differs from the other answers? Also know that Code-only answers are not useful in the long run. – 7uc1f3r Sep 19 '20 at 08:59
0

i found a solution to resolve this issue

here is the Example code

import React from 'react';
import Home from './HomeComponent/Home';
import { Switch, Route } from 'react-router';
import { BrowserRouter } from "react-router-dom";
class App extends React.Component{
render(){
return(
         <BrowserRouter>
           <Switch>
            <Route path="/" render={props => (
                <Home {...props}/>
            )}/>
          </Switch>
        </BrowserRouter>
    )
  }
}
export default App;
0

My NavLink and Route are in separate files, in order to stop this error I had to wrap them both in BrowserRouter:

import React from 'react';
import { NavLink, BrowserRouter } from 'react-router-dom'
const MainNav = () => (
  <BrowserRouter>
      <nav>
      <ul>
        <li>
          <NavLink exact activeClassName='current' to='/'>Home</NavLink>
        </li>
        <li>
          <NavLink exact activeClassName='current' to='/users'>Users</NavLink>
        </li>
      </ul>
    </nav>
  </BrowserRouter>
);


export default MainNav;

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from '../views/HomeView';
import UsersView from '../views/UsersView';
import { BrowserRouter } from 'react-router-dom';

const MainRoutes = () => (
  <BrowserRouter>
    <Switch>
      <Route exact path='/' component={Home}></Route>
      <Route exact path='/users' component={UsersView}></Route>
    </Switch>
  </BrowserRouter>
);

export default MainRoutes;`
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61