45

I want to hide the navbar in a login page.

I did it actually, but I can't see the navbar on other pages.

This code is part of My App.jsx file.

I make history in App's state. And I hide navbar, when this pathname is '/' or '/login'.

It works!

But then I typed the ID and password, and clicked the login button, got 'success' result, and navigated to '/main'.

Now I can't see navbar in main component too.

How can I do this?

Sorry about my short english. If you can't understand my question, you can comment.

constructor(props) {
  super(props);
  this.state = {
    isAlertOpen: false,
    history: createBrowserHistory(),
  };
  this.toggleAlert = this.toggleAlert.bind(this);
}

<BrowserRouter>
  <div className="App">
    {this.state.history.location.pathname === '/' || this.state.history.location.pathname === '/login' ? null
      : <Header toggleAlert={this.toggleAlert} />}
    <div className="container">
      {this.state.history.location.pathname === '/' || this.state.history.location.pathname === '/login' ? null
        : <Navbar />}
      <Route exact path="/" render={() => <Redirect to="/login" />} />
      <Route path="/login" component={Login} />
      <Route path="/main" component={Main} />
      <Route path="/user" component={User} />
      <Route path="/hw-setting" component={Setting} />
      <Route path="/hw-detail/:id" component={HwDetail} />
      <Route path="/gas-detail/:id" component={GasDetail} />
      {this.state.isAlertOpen ? <Alert /> : null}
    </div>
  </div>
</BrowserRouter>

login(event) {
  event.preventDefault();
  userService.login(this.state.id, this.state.password).subscribe(res => {
    if (res.result === 'success') {
      global.token = res.token;
      this.props.history.push('/main');
    } else {
      alert(`[ERROR CODE : ${res.statusCode}] ${res.msg}`);
    }
});
jkdev
  • 11,360
  • 15
  • 54
  • 77
Jong-Hyen Kim
  • 765
  • 2
  • 6
  • 18
  • I suspect the `history` you are instantiating yourself is not being kept in sync with the one created by `react-router`. If you want access to the history, you should probably use the [withRouter](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/withRouter.md) HOC – Mario F Nov 14 '17 at 09:28

7 Answers7

92

You could structure your Routes differently so that the Login component doesn't have the Header Like

<BrowserRouter>
  <Switch>
  <div className="App">
    <Route exact path="/(login)" component={LoginContainer}/>
    <Route component={DefaultContainer}/>

  </div>
  </Switch>
</BrowserRouter>

const LoginContainer = () => (
  <div className="container">
    <Route exact path="/" render={() => <Redirect to="/login" />} />
    <Route path="/login" component={Login} />
  </div>
)


 const DefaultContainer = () => (
    <div>
    <Header toggleAlert={this.toggleAlert} />
    <div className="container">
      <Navbar />
      <Route path="/main" component={Main} />
      <Route path="/user" component={User} />
      <Route path="/hw-setting" component={Setting} />
      <Route path="/hw-detail/:id" component={HwDetail} />
      <Route path="/gas-detail/:id" component={GasDetail} />
      {this.state.isAlertOpen ? <Alert /> : null}
    </div>
    </div>
 )
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Thanks for your answer. I change it as you did. But `navbar` don't hide. – Jong-Hyen Kim Nov 14 '17 at 09:37
  • How to do it in React 15? – Manish Mahajan Nov 30 '17 at 11:48
  • @Dramorian, Why do say its dependent on React version, I don't see any change. Do you get some error when you try it with React 15 – Shubham Khatri Nov 30 '17 at 12:23
  • @ShubhamKhatri Sorry. its not related to React version. More of its related to implementation of routing v3 and routing4. Like ReactRoute v4 not able to define multiple routes in V3 – Manish Mahajan Dec 07 '17 at 15:04
  • @ShubhamKhatri How do you render a default component in this case? I have a page not found kind of component that should be show for incorrect routes. Not sure how to make that work. – Sourabh86 May 24 '18 at 12:54
  • @Sourabh86 For the top level Route, you can have a default Route within the switch that renders if not others match, however if some internal Routes do not match, if kind of little tricky and lengthy to explain, can you ask a separate question for it – Shubham Khatri May 24 '18 at 13:01
  • 1
    How can I add no match route in this? – Saumay Paul May 20 '20 at 01:45
  • To handle not found pages, we can pass props to `DefaultContainer` and render navbar based on conditions using `props```` { false && } // this will not show the navbar. the false condition evaluation can be done based on the props.location ``` – Ashish Gaude Jul 29 '20 at 08:00
  • This was the best example. I was struggling to make my routing work correctly. Thank you.. – newbie_learner Oct 20 '20 at 16:33
  • @ShubhamKhatri Hey man, sorry to bother you after all this time, but you seem to know your way around react router. I am having a annoying little problem, i separeted my logic just as you did, but inside my switch, when i wrapped the routes in a div it renders my navbar and my 404 route that is also inside the switch no matter what route i am in, but when i dont wrap inside a div it only renders the first container inside the switch. If you could help me out. Thanks in advance . – PedroMiotti Nov 16 '20 at 00:10
39

As of the latest release of React Router v6, it is no longer possible to pass a <div> component inside the Routes (v6) aka Switch(v5 or lower) to render a Navbar. You will need to do something like this:

  1. Create two Layout components. One simply renders a Nav and the other one does not. Suppose we name them
  • <WithNav />
  • <WithoutNav />
  1. You will need to import <Outlet /> from the React router and render inside the Layout components for the routes to be matched.

Then in your App or where ever you have your Router you will render like below ....


// WithNav.js (Stand-alone Functional Component)
import React from 'react';
import NavBar from 'your navbar location';
import { Outlet } from 'react-router';

export default () => {
  return (
    <>
      <NavBar />
      <Outlet />
    </>
  );
};


// WithoutNav.js (Stand-alone Functional Component)
import React from 'react';
import { Outlet } from 'react-router';

export default () => <Outlet />


// your router (Assuming this resides in your App.js)

      <Routes>
        <Route element={<WithoutNav />}>
          <Route path="/login" element={<LoginPage />} />
        </Route>
        <Route element={<WithNav />}>
          <Route path="/=example" element={<Example />} />
        </Route>
      </Routes>

LoginPage will not have a nav however, Example page will

Ash
  • 585
  • 7
  • 15
  • 1
    Very helpful answer for v6. If I could offer some minor feedback, updating your layouts to show that they are separate functions would offer a more complete example. – elilambnz Jan 10 '22 at 06:35
  • @elilambnz - Thanks for the feedback. Adjusted for clarity – Ash Jan 10 '22 at 14:50
  • Thanks this works. My NavBar component includes an AppBar and a Drawer. (both from MUI). Some content of the components inside WithNav are not shown because the Drawer is above them. Any thoughts on fixing this? – Vizard Apr 21 '22 at 20:41
  • Great answer and update for the v6. How about you update the answer to be beginner friendly. – adamu_fura Jul 29 '22 at 19:45
  • This is just what I needed. `Outlet` was the missing puzzle piece – zwbetz Oct 19 '22 at 16:03
32

Simplest way is use div tag and put components in which you want navbar and put login route component outside div tag:

<div className="App">
  <Router>

    <Switch>
      <Route exact path="/" component={Login} />
      <div>
        <NavBar />
   
        <Route exact path="/addproduct" component={Addproduct}></Route>
        <Route exact path="/products" component={Products}></Route>
     
      </div>

    </Switch>
  </Router>

</div>
Vijit Nagar
  • 321
  • 3
  • 2
2

The same idea with Ash for react router v6 but without creating two new components.

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<Login />} />
        <Route
          element={
            <>
              <Navbar />
              <Outlet />
            </>
          }
        >
          <Route path="/" element={<Home />}></Route>
          <Route path="/products" element={<Products />}></Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}
Steve Lukis
  • 420
  • 4
  • 10
1

Put the Route with path="/" below every other routes :

<Switch>
  <Route path="/login" component={Login} />
  <Route path="/" component={Home} />
</Switch>

It will work.

Aqeel
  • 804
  • 1
  • 11
  • 15
0

I'm was trying to solve this problem, what i did was add component helmet, to install it use : yarn add react-helmet --save.

import {Helmet} from 'react-helmet';
<Helmet>
   <script src="https://kit.fontawesome.com/.....js" crossorigin="anonymous"></script>
</Helmet>
-4

The accepted answer has problem if you need to add other default route within the switch if no other route matches, e.g., 404 page, not found page.

I ended up using simple css to hide navigation bar inside my login page.

class LoginPage extends React.Component<>{

   ...

   // Hide navigation bar in login page. Do it inside ComponentDidMount as we need to wait for navbar to render before hiding it.
   componentDidMount(){
      document.getElementById('navigation-bar')!.style.display = "none";
   }

   componentWillUnmount(){
      document.getElementById('navigation-bar')!.style.display = "flex";
   }

   render(){
      return(
          // your login/signup component here
          ...
      )
   }

}