72

I am trying to create a simple Webapp using ReactJS, and I wanted to use the Navbar provided by React-Bootstrap.

I created a Navigation.js file containing a class Navigation to separate the Navbar and the Routing from the App.js file. However, both parts do not seem to work. When I load the page, it is just empty, there is no Navbar. Can anyone spot a mistake?

Navigation.js:

import React, { Component } from 'react';
import { Navbar, Nav, Form, FormControl, Button, NavItem } from 'react-bootstrap';
import { Switch, Route } from 'react-router-dom';
import { Home } from './Page';

class Navigation extends Component {
    render() {
        return (
            <div>
                <div>
                    <Navbar>
                        <Navbar.Brand href="/">React-Bootstrap</Navbar.Brand>
                        <Navbar.Collapse>
                            <Nav className="mr-auto">
                                <NavItem eventkey={1} href="/">
                                    <Nav.Link href="/">Home</Nav.Link>
                                </NavItem>
                            </Nav>
                            <Form inline>
                                <FormControl type="text" placeholder="Search" className="mr-sm-2" />
                                <Button variant="outline-success">Search</Button>
                            </Form>
                        </Navbar.Collapse>
                    </Navbar>
                </div>
                <div>
                    <Switch>
                        <Route exact path='/' component={Home} />
                        <Route render={function () {
                            return <p>Not found</p>
                        }} />
                    </Switch>
                </div>
            </div>
        );
    }
}

export default Navigation;

App.js:

import React, { Component } from 'react';
import Navigation from './components/routing/Navigation';



class App extends Component {
  render() {
    return (
      <div id="App">
        <Navigation />
      </div>
    );
  }
}

export default App;

I tried using a NavItem containing a LinkContainer from react-router-bootstrap already, which led to the same result.

Just for completeness, Page.js:

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

export const Page = ({ title }) => (
    <div className="App">
      <div className="App-header">
        <h2>{title}</h2>
      </div>
      <p className="App-intro">
        This is the {title} page.
      </p>
      <p>
        <Link to="/">Home</Link>
      </p>
      <p>
        <Link to="/about">About</Link>
      </p>
      <p>
        <Link to="/settings">Settings</Link>
      </p>
    </div>
);


export const About = (props) => (
    <Page title="About"/>
);

export  const Settings = (props) => (
    <Page title="Settings"/>
);

export const Home = (props) => (
    <Page title="Home"/>
);
NotAName
  • 865
  • 1
  • 6
  • 8

7 Answers7

227

First of all, in your snippets it doesn't seem like you're wrapping your code in a Router, so you should make sure that you're doing that inside App or in ReactDOM.render:

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

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>, 
  rootElement
  );

Next, your specific problem is that you're rendering react-bootstrap's Nav.Link instead of react-router's Link component, so the router is not picking up your route changes. Fortunately, react-bootstrap provides a render prop in most of its components to specify which component or element you want to render if you don't want the default. Switch to something like this:

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

class Navigation extends Component {
  render() {
    return (
      <div>
        <div>
          <Navbar>
            <Navbar.Brand as={Link} to="/" >React-Bootstrap</Navbar.Brand>
            <Navbar.Collapse>
              <Nav className="mr-auto">
                <NavItem eventkey={1} href="/">
                  <Nav.Link as={Link} to="/" >Home</Nav.Link>
                </NavItem>
              </Nav>
              <Form inline>
                <FormControl type="text" placeholder="Search" className="mr-sm-2" />
                <Button variant="outline-success">Search</Button>
              </Form>
            </Navbar.Collapse>
          </Navbar>
        </div>
        <div>
          <Switch>
            <Route exact path='/' component={Home} />
            <Route render={function () {
              return <p>Not found</p>
            }} />
          </Switch>
        </div>
      </div>
    );
  }
}
SrThompson
  • 5,568
  • 2
  • 17
  • 25
  • 14
    Thank you sir! the as={Link} and to"..." was what I was missing. It's strange that the bootstrap docs don't mention this at all. – mBrice1024 May 09 '20 at 03:41
  • It looks like this should have `Nav.Item` instead of `NavItem` – kugo2006 Oct 20 '20 at 06:22
  • 1
    @kugo2006 They're the same component, but yes, it's one less import with `Nav.Item`. Also, the question was about `Nav.Link` and the router, and it looks like I just copied the snippet from OP's question and fixed it, so I didn't do a lot of refactoring :) – SrThompson Oct 20 '20 at 22:50
  • I am wondering where I can find that in the docs. Please somebody paste link to the docs where is that mentioned for reference. How should we know that there is 'as' property? – Georgi Georgiev Jan 30 '21 at 13:21
  • 2
    @GeorgiGeorgiev https://react-bootstrap.github.io/components/navs/#nav-link-props – SrThompson Feb 01 '21 at 13:27
  • The only issue this fix introduces is that the Navbar no longer collapses when you click a link (not even if collapseOnSelect is present on Navbar). – nxasdf Apr 30 '22 at 18:23
42

For those who have a problem with styling Link component from react-router-dom in react-bootstrap navbar, simply add className="nav-link", like this:

<Link to="/" className="nav-link">Home</Link>

instead of

<Nav.Link href="/">Home</Nav.Link>

DedaDev
  • 4,441
  • 2
  • 21
  • 28
10

I hope I'm no late to help some other people trying to solve this.

You can use the NavLink, instead of as={NavLink}. It will render with the same behavior of Link, but will "watch" the router URL to define which link is indeed active:

<Nav defaultActiveKey="/bills" as="ul">
      <Nav.Item as="li">
        <Nav.Link as={NavLink} to="/bills">Dividas</Nav.Link>
      </Nav.Item>
      <Nav.Item as="li">
        <Nav.Link as={NavLink} to="/other">Em construção</Nav.Link>
      </Nav.Item>
    </Nav>
Rodrigo Borba
  • 1,334
  • 1
  • 14
  • 21
2
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

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

and Navbar.js:

import React from 'react';
import {Container,Navbar,Nav,NavItem } from 'react-bootstrap';
import {Link} from 'react-router-dom';

<Nav className="ml-auto">
<NavItem>   <Link className="nav-link"   to="/">Home</Link> </NavItem> 
<NavItem>   <Link  className="nav-link" to="/about">About</Link> </NavItem> 
<NavItem>    <Link className="nav-link"   to="/contact">Contact</Link> </NavItem> 
</Nav>

This resolved the: <Link> outside a <Router> error for me.

h3t1
  • 1,126
  • 2
  • 18
  • 29
Preeti Maurya
  • 193
  • 2
  • 4
2

Currently, with react v18 and react-router v6.4, the approach is working for me is a bit different.

To make the links properly work with react-route use as={Link} in the Nav.Link item.

And to make the tabs highlight you need to use eventKey as described in the documentation: EventKey is used to determine and control the active state of the parent Nav

Here is an example.

Notice that the changes also affect the Navbar.Brand component.

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

const AppNavbar = () => {
  const location = useLocation();

  const activeKey = location.pathname === '/' ? '/projects' : location.pathname;

  return (
    <Navbar expand="lg" className="theme-navbar">
      <Container>
        <Navbar.Brand as={Link} to="/">
          My Projects
        </Navbar.Brand>
        <Navbar.Toggle aria-controls="basic-navbar-nav" />
        <Navbar.Collapse id="basic-navbar-nav">
          <Nav activeKey={activeKey} className="me-auto">
            <Nav.Link as={Link} eventKey="/projects" to="/projects">
              Projects
            </Nav.Link>
            <Nav.Link as={Link} eventKey="/work" to="/work">
              Ongoing Tasks
            </Nav.Link>
          </Nav>
        </Navbar.Collapse>
      </Container>
    </Navbar>
  );
};
kiril
  • 4,914
  • 1
  • 30
  • 40
1

Having found myself with a project of non-trivial size, and one that already had jQuery as a dependency I was able to gracefully solve the react-router / react-bootstrap mismatch with an event listener on the document.

This has one advantage over other solutions in that it requires no changes to the current markup. However, one may need to write some additional logic guarding the history.push call depending on needs. One may also need to expand this to guard for the target attribute, e.g. target="_blank".

If jQuery is not desired, one may be able to write a vanilla JS implementation with document.addEventListener without much additional effort.

// react-router@5
// usage of history may vary depending on version
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';

const history = createBrowserHistory();

// IIFE scoping jQuery for us
(($) => {
  // Wait for document ready
  $(() => {
    $(document).on('click', '[href]', (event) => {
      // Only target links targeting our application's origin
      if (event.currentTarget.href.indexOf(window.location.origin) === 0) {
        // Prevent standard browser navigation
        event.preventDefault();

        const path = event.currentTarget.href.slice(window.location.origin.length);

        history.push(path);
      }
    });
  });
})(jQuery);

const Routing = (props) => (
  <Router history={ history }>
    ...
  </Router>
);
-4

i think you forgot to include bootstrap css, refer to the stylesheets section of the following doc

https://react-bootstrap.github.io/getting-started/introduction/

or just add the following to ur index.html

<link
  rel="stylesheet"
  href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
  integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
  crossorigin="anonymous"
/>
ravibagul91
  • 20,072
  • 5
  • 36
  • 59
Noman Hassan
  • 381
  • 1
  • 10