Pavel Perevezencev
Pavel Perevezencev

Reputation: 2978

Invariant failed: You should not use <Route> outside a <Router>

I use react-router-dom for routing in my React application. Part of my app extracted in another package. List of dependencies looks like this:

./app/dashboard/package.json

{
  "dependencies": {
    "@app/components": "^1.0.0",
    "react": "^16.8.5",
    "react-dom": "^16.8.5",
    "react-router-dom": "^5.0.0"
  }
}

./app/components/package.json

{
  "peerDependencies": {
    "react-router-dom": "^5.0.0"
  }
}

When I use components from @app/components which require components from react-router-dom I getting this errors:

Uncaught Error: Invariant failed: You should not use <Route> outside a <Router> 
The above error occurred in the <Context.Consumer> component:
Uncaught (in promise) Error: Invariant failed: You should not use <Route> outside a <Router>

Why throws this error? In App.js I use BrowserRouter

import React, { Suspense } from 'react';
import { Switch, Route } from 'react-router-dom';
import { Placeholder } from '@app/components';

const Auth = React.lazy(() => import(/* webpackPrefetch: true */ './pages/Auth'));
const Index = React.lazy(() => import(/* webpackPrefetch: true */ './pages/Index'));

const App = () => (
  <Suspense fallback={<Placeholder />}>
    <Switch>
      <Route path="/auth" component={Auth} />
      <Route path="/" component={Index} />
    </Switch>
  </Suspense>
);

export default App;

client.jsx

import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

import App from './App';

render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root'),
);

Upvotes: 94

Views: 215103

Answers (25)

santosh sutar
santosh sutar

Reputation: 156

You should include Route tag, Link tag inside the Router tag.

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

const Home = () => {
  return (
    <div>
      <p>Home</p>
    </div>
  );
};

const About = () => {
  return (
    <div>
      <p>About</p>
    </div>
  );
};

const Contact = () => {
  return (
    <div>
      <p>Contact</p>
    </div>
  );
};

class App extends Component {
  render() {
    return (
      <Router>
        <div>
           <h1>W3Adda - Simple SPA</h1>
           <nav>
             <ul>
               <li>
                 <Link to="/">Home</Link>
               </li>
               <li>
                 <Link to="/about">About</Link>
               </li>
               <li>
                 <Link to="/contact">Users</Link>
               </li>
             </ul>
           </nav>

           <Route path="/" exact component={Home} />
           <Route path="/about" component={About} />
           <Route path="/contact" component={Contact} />
         </div>
      </Router>
    );
  }
}

export default App;

Upvotes: 10

I managed to solve the problem by removing <BrowserRouter> from router.js and including it in app.js

export default function Routes() {
  return (
    <Switch>
      <Route path="/" exact component={Home} />
      <Route path="/book" component={Book} />
      <Route path="/client" component={Client} />
      <Route path="/rent" component={Rent} />
      <Route path="/dash" component={Dash} />
    </Switch>
  );
}

function App() {
  return (
    <div>
      <BrowserRouter>
       <Header></Header>
       <Routes/>
      </BrowserRouter>
    </div>
  );
}

Upvotes: 0

zhyp
zhyp

Reputation: 349

For anyone having this problem while unit testing here is something that worked out for me:

function mountComponentWithRouter(Component, props) {
  const Teste = withRouter(prop => <Component {...prop} {...props} />);

  return mount(
    <HashRouter>
      <Teste />
    </HashRouter>,
    {
      context: { router },
      childContextTypes: { router: PropTypes.object.isRequired }
    }
  );
}

With this any react-router-dom component inside Component will have all of those props (pathname, hash, state, search) that are usually passed down naturally when running the application.

Upvotes: 0

KARTHIKEYAN.A
KARTHIKEYAN.A

Reputation: 20118

I had same problem while I wrape App component with withRouter

Error:

import React, { Suspense } from 'react';
import { BrowserRouter, Switch, Route, withRouter } from 'react-router-dom';
import { Placeholder } from '@app/components';

const Auth = React.lazy(() => import(/* webpackPrefetch: true */ './pages/Auth'));
const Index = React.lazy(() => import(/* webpackPrefetch: true */ './pages/Index'));

const App = () => (
  <BrowserRouter>
  <Suspense fallback={<Placeholder />}>
    <Switch>
      <Route path="/auth" component={Auth} />
      <Route path="/" component={Index} />
    </Switch>
  </Suspense>
  </BrowserRouter>
);

export default withRouter(App);

Solution:

we already using Switch component, after that I have removed withRouter

    import React, { Suspense } from 'react';
    import {BrowserRouter, Switch, Route } from 'react-router-dom';
    import { Placeholder } from '@app/components';
    
    const Auth = React.lazy(() => import(/* webpackPrefetch: true */ './pages/Auth'));
    const Index = React.lazy(() => import(/* webpackPrefetch: true */ './pages/Index'));
    
    const App = () => (
      <BrowserRouter>
      <Suspense fallback={<Placeholder />}>
        <Switch>
          <Route path="/auth" component={Auth} />
          <Route path="/" component={Index} />
        </Switch>
      </Suspense>
      </BrowserRouter>
    );
    
    export default App;

Upvotes: 0

I fixed that problem just importing the BrowserRouter from react-router-dom in index.js and adding:

<BrowserRouter>
   <App>
 </BrowserRouter>

within:

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
 document.getElementById('root'));

Upvotes: 16

hang-coder
hang-coder

Reputation: 440

The simplest answer is right inside official doc from reactstrap for Navbar, see this website https://reactstrap.github.io/components/navbar/

They guided that we need to use this import

import {NavLink} from 'reactstrap';

Upvotes: -1

Dzmitry Kulahin
Dzmitry Kulahin

Reputation: 1880

If you're using GatsbyJS and getting this error just change your

import { Link } from "react-router-dom";

to

import { Link } from 'gatsby';

Upvotes: 1

Sandra
Sandra

Reputation: 479

I had a similar situation with the main app created with CRA and components library. We have two instances of react-router-dom working independently. Component from library uses <Link> while parent app has <Router>.

Inspired by @Pavel answer I discovered that issue can be solved by adding aliases. If you are using webpack, you can change the way it will resolve the react-router-dom module. You can overwrite the default order in which webpack will look for your dependencies and make your parent application node_modules more prioritized than component node module resolution order:

aliases.js

const fs = require('fs');
const path = require('path');

const directory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(directory, relativePath);

module.exports = {
  react: resolveApp('node_modules/react'),
  'react-router-dom': resolveApp('node_modules/react-router-dom'),
};

that are used by config-override.js to override default CRA settings:

const { addWebpackAlias, override, removeModuleScopePlugin } = require('customize-cra');
const aliases = require('./aliases');

module.exports = override(removeModuleScopePlugin(), addWebpackAlias(aliases));

or for webpack.config.js (if it's not CRA):

resolve: {
  alias: {
    "react-router-dom": path.resolve(appFolder, "node_modules", "react-router-dom"),
 }
}

Upvotes: 4

breakfastatiffs
breakfastatiffs

Reputation: 341

I had this problem whilst testing and solved it by wrapping my test component with Routers.

import React from 'react';
import ReactDom from 'react-dom';
import Header from '../components/Header/Header';
import { BrowserRouter } from 'react-router-dom';

it('renders Header without crashing', () => {
  const div = document.createElement('div');

  ReactDom.render(
    <BrowserRouter>
      <Header />
    </BrowserRouter>, 
  div);

  ReactDom.unmountComponentAtNode(div);
});

Jest, Enzyme: Invariant Violation: You should not use or , To test a component (with Jest) that contains and withRouter you need to import Router in you test, not in your component import { BrowserRouter as Invariant Violation: You should not use or withRouter() outside a According to react router 4 docs, the components are considered valid, where I can create components composed of s, then import them into another component and place inside a .

Upvotes: 34

Yura Rozhko
Yura Rozhko

Reputation: 29

just change "react-router" on "react-router-dom"

import {Route} from "react-router";

to

import {Route} from "react-router-dom";

Upvotes: 1

Thomas Sample
Thomas Sample

Reputation: 1

I had a similar problem when trying to replace Rails views in a Solidus app.

The solution for me was to edit the vies to use the React Router that I had just added, but apparently hadn't fully configured.

For example, I edited views/spree/home/index.html from:

<%= javascript_pack_tag 'application' %>
<%= react_component("Home/Index") %>

to:

<%= javascript_pack_tag 'application' %>
<%= react_component("Routes") %>

For more reference, here is my javascript/components/Route.js:

import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from "./Home/Index";
import Product from "./Products/Show";

class Routes extends React.Component {
  render() {
    return (
      <Router>
        <Route exact path="/" component={Home} />
        <Route path="/products/:slug" component={Product} />
      </Router>
    );
  };
};

export default Routes;

Upvotes: 0

Lokeshwaran
Lokeshwaran

Reputation: 21

I have solved this issue by importing

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

and wrapped all the Route components under the Router component

 <Router>
     <Route exact path='/' component={HomePage} />
 </Router>

Upvotes: 0

Ali Raza
Ali Raza

Reputation: 1

Use Router outside of your components. Wrap all components inside the Router then will able to use Link in your components.

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

<Router>
        <Appbar />
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/mac" exact component={Mac} />
        </Switch>
       <Footer />
      </Router>

Upvotes: 0

Tanvir Islam Streame
Tanvir Islam Streame

Reputation: 460

Actually the issue was in your root component where you are doing routing, you should not add any component outside of Router tag in the component, that component only expect single/multiple component wraped by Route under Router in that components.

wrong code (but it will run, but you are creating bug) -

function App() {
  return (
    <div className="App">
      <Header/>
      <Router>
        <Switch>
          <Route path="/" exact>
            <Home />
          </Route>
        </Switch>
      </Router>
    </div>
  );
}

I only remove <Header/> , it solved the issue.

write code -

function App() {
  return (
    <div className="App">
      <Router>
        <Switch>
          <Route path="/" exact>
            <Home />
          </Route>
        </Switch>
      </Router>
    </div>
  );
}

Upvotes: 1

Maximus Kai
Maximus Kai

Reputation: 1

Using the <Link> tag in a component that doesn't have the imported could give this error, try importing the BrowserRouter from 'react-router-dom'

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

then make sure to wrap your linked tags in the tag

Upvotes: 0

aixecador
aixecador

Reputation: 876

I had the same problem with my Create React App when running tests and I solved it by placing <Router></Router inside App.js instead of in Index.js as I have always done.

Before:

Index.js

ReactDOM.render(
    <React.StrictMode>
        <GlobalStyle />
        <Router>
            <App />
        </Router>
    </React.StrictMode>,
document.getElementById('root')
);

App.js

return (
    <div className="App">
        <Header />
        <Route path="/blabla" component={Whatever}
    </div>
)

After:

Index.js:

ReactDOM.render(
    <React.StrictMode>
        <GlobalStyle />
        <App />
    </React.StrictMode>,
document.getElementById('root')
);

App.js:

return (
    <div className="App">
        <Router>
            <Header />
            <Route path="/blabla" component={Whatever}
        </Router>
    </div>
)

Upvotes: 2

Pranay kumar
Pranay kumar

Reputation: 2197

I solve this error, by wrapping my parent component inside Router.

Before solving error

<Header />
<div className="col-md-12 pd-0-0">
  <div className="row">
    <Sidebar />
    <div className="col-sm-10 col-md-10 pd-0-0" style={style}>
      <Router>
        <Switch>
          <Route path="/dashboard/check-in" component={CheckIn} />
          <Route path="/dashboard/deals" component={Deals} />
          <Route path="/dashboard/events" component={Events} />
          <Route path="/dashboard/invoice" component={Invoice} />
          <Route path="/dashboard/notification" component={Notification} />
          <Route path="/dashboard/profile" component={Profile} />
          <Route path="/dashboard/redemption" component={Redemptions} />
          <Route path="/dashboard/restriction-management" component={RestrictionManagement} />
        </Switch>
      </Router>
    </div>
  </div>
</div>

After solving error

<Router>
  <Header />
  <div className="col-md-12 pd-0-0">
    <div className="row">
      <Sidebar />
      <div className="col-sm-10 col-md-10 pd-0-0" style={style}>
        <Switch>
          <Route path="/dashboard/check-in" component={CheckIn} />
          <Route path="/dashboard/deals" component={Deals} />
          <Route path="/dashboard/events" component={Events} />
          <Route path="/dashboard/invoice" component={Invoice} />
          <Route path="/dashboard/notification" component={Notification} />
          <Route path="/dashboard/profile" component={Profile} />
          <Route path="/dashboard/redemption" component={Redemptions} />
          <Route path="/dashboard/restriction-management" component={RestrictionManagement} />
        </Switch>
      </div>
    </div>
  </div>
</Router>

Upvotes: 1

user2552981
user2552981

Reputation: 379

I solved this problem by changing:

import {Route, Switch} from "react-router";

to

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

just add -dom.

Upvotes: 37

Maximilian Kern
Maximilian Kern

Reputation: 1

In my case, this error was caused due to mixing up usage of things (withRouter, MemoryRouter) from react-router and react-router-dom. By using only things from react-router-dom, the invariant error vanished. Hope this helps anybody.

Upvotes: 0

Marko Perendio
Marko Perendio

Reputation: 89

I had a similar problem with Redirect component. It turned out that I called Redirect from 'react-router' instead of 'react-router-dom'.

Error: Invariant failed: You should not use <Redirect> outside a <Router>

Upvotes: 7

Mehdi Dehghani
Mehdi Dehghani

Reputation: 11601

I got this during testing my component which linked to the project (using npm link) and because of that react-router-dom loaded more than once, following solution works in my case:

Inside webpack.config.js I added:

resolve: {
    alias: {        
        'react-router-dom': path.resolve('./node_modules/react-router-dom')
    }
}

Upvotes: 9

Juan
Juan

Reputation: 816

I solved this cryptic error by deleting the dependency of 'react-router-dom' from MyComponent. I deleted 'node_modules/react-router-dom' and from 'package.json' My App is structured as follows:

    AppFolder
    ├── README.md
    ├── build
    ├── node_modules
    │   ├── react-router-dom
    ├── package-lock.json
    ├── package.json
    ├── public
    │   ├── favicon.ico
    │   ├── index.html
    │   └── manifest.json
    └── src
        ├── App.css
        ├── App.js
        ├── App.test.js
        ├── index.js
        ├── redux
        │   ├── reducers.js
        │   └── store.js
        └── serviceWorker.js


    MyComponent
    ├── README.md
    ├── dist
    ├── node_modules
    │   ├── react-router-dom
    ├── package-lock.json
    ├── package.json
    ├── rollup.config.js
    └── src
        ├── index.js
        ├── styles.css
        └── test.js

This is the source code for App.js

import MyComponent from 'MyComponent'

export default App(){
 return (
 <div className="App">
    <HashRouter>
      <div>
        <header className="App-header">

        </header>
        <Route path="/home" component={MyComponent}/>
      </div>
    </HashRouter>
  </div>)
}

This is the export for MyComponent

export default withRouter(MyComponent);

If 'react-router-dom' is left in the component folder, then the error appears.

Upvotes: 1

Pavel Perevezencev
Pavel Perevezencev

Reputation: 2978

Trouble with 2 instance of React

Upvotes: 17

JaY
JaY

Reputation: 13

You have a hanging comma at the end of the render call after document.getElementById('root').

render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root'),
);

Upvotes: -12

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15698

You need import the named export Router as well from react-router-dom and wrap it around your Switch/Route components.

const App = () => (
  <Router>
    <Suspense fallback={<Placeholder />}>
      <Switch>
        <Route path="/auth" component={Auth} />
        <Route path="/" component={Index} />
      </Switch>
    </Suspense>
  </Router>
);

Upvotes: 7

Related Questions