Skip to main content
/
/
/
Nav

Nav

import Nav from "@intility/bifrost-react/Nav";
Useful if you're creating a new app from scratch.

Responsive navigation

By default, the <Nav> provides a responsive layout for your app with a logo, sidebar, top bar, mobile nav and hamburger menu button.

  1. Mobile - 0px-599px
    • Sidebar is hidden
    • Top bar is visible, with logo, top contents, and menu toggle button
    • Top bar hides when scrolling down, and re-appears when scrolling upwards
    • Menu button in top bar toggles fullscreen mobile menu
    • Mobile navigation menu displays content from side prop. If you want completely separate navigation from sidebar you can use mobile prop instead.
  2. Tablet - 600px-1599px
    • Same as mobile, but the menu is placed on the left hand side with an overlay covering the rest of the page
  3. Desktop - 1600px and wider
    • No hamburger button
    • Sidebar is displayed only if side prop has content
    • Top bar is displayed only if top prop has content
<Nav // if you only supply a string to `logo` you'll get an "it" logo graphic logo="Demo App" side={ <> <a href="/"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="/profile"> <Nav.Item icon={faUser}>Profile</Nav.Item> </a> </> } top={ <> <a href="/profile" title="User profile"> <Nav.Item icon={faUser} /> </a> <button type="button"> <Nav.Item>Button</Nav.Item> </button> </> } > Your app content goes here </Nav>
<Nav // if you only supply a string to `logo` you'll get an "it" logo graphic logo="Demo App" side={ <> <a href="/"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="/profile"> <Nav.Item icon={faUser}>Profile</Nav.Item> </a> </> } top={ <> <a href="/profile" title="User profile"> <Nav.Item icon={faUser} /> </a> <button type="button"> <Nav.Item>Button</Nav.Item> </button> </> } > Your app content goes here </Nav>
Skip to main content
Your app content goes here

<Nav.Logo> is a helper component to make it easier to style the logo and app name uniformly across applications.

<Nav.Logo logo={<img src="myLogo.svg" alt="" />}>App Name</Nav.Logo>
<Nav.Logo logo={<img src="myLogo.svg" alt="" />}>App Name</Nav.Logo>

<Nav.Logo logo> also accepts a string URL which is equivalent to the above.

<Nav.Logo logo="myLogo.svg">App Name</Nav.Logo>
<Nav.Logo logo="myLogo.svg">App Name</Nav.Logo>

The logo prop on <Nav> lets you supply any JSX content which will be placed in the top bar.

We recommend wrapping <Nav.Logo> in a.bf-neutral-link linking to the home page (if you're using a routing lib, you probably want to use <Link> instead of <a>)

<Nav logo={ <a href="/" className="bf-neutral-link"> <Nav.Logo logo="myLogo.svg">App Name</Nav.Logo> </a> } top={...} />
<Nav logo={ <a href="/" className="bf-neutral-link"> <Nav.Logo logo="myLogo.svg">App Name</Nav.Logo> </a> } top={...} />

If you only have a standalone sidebar, the logo will be placed in the top of the sidebar. Mobile devices always have a top bar.

<Nav logo={ <a href="/" className="bf-neutral-link"> <Nav.Logo logo="myLogo.svg">App Name</Nav.Logo> </a> } side={...} />
<Nav logo={ <a href="/" className="bf-neutral-link"> <Nav.Logo logo="myLogo.svg">App Name</Nav.Logo> </a> } side={...} />
Skip to main content

Collapsible sidebar

If you want the sidebar to be collapsible, first make sure that each menu item is a <Nav.Item> or <Nav.Group> with an icon.

Pass a boolean state to the sideProps.collapsed prop, and a function to the sideProps.onCollapsedChange event.

Consider using use-local-storage-state if you want to persist the state between page reloads.

import { faEnvelopeOpenText, faHome } from "@fortawesome/free-solid-svg-icons"; import Nav from "@intility/bifrost-react/Nav"; import { useState } from "react"; export default function NavSideCollapseDemo() { const [sideCollapsed, setSideCollapsed] = useState(false); return ( <Nav logo="Demo App" sideProps={{ collapsed: sideCollapsed, onCollapsedChange: (collapsed) => setSideCollapsed(collapsed), }} side={ <> <a href="#path"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="#path"> <Nav.Item icon={faEnvelopeOpenText}>Tickets</Nav.Item> </a> </> } > Your app content goes here </Nav> ); }
import { faEnvelopeOpenText, faHome } from "@fortawesome/free-solid-svg-icons"; import Nav from "@intility/bifrost-react/Nav"; import { useState } from "react"; export default function NavSideCollapseDemo() { const [sideCollapsed, setSideCollapsed] = useState(false); return ( <Nav logo="Demo App" sideProps={{ collapsed: sideCollapsed, onCollapsedChange: (collapsed) => setSideCollapsed(collapsed), }} side={ <> <a href="#path"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="#path"> <Nav.Item icon={faEnvelopeOpenText}>Tickets</Nav.Item> </a> </> } > Your app content goes here </Nav> ); }
Skip to main content
Your app content goes here

Standalone sidebar

If you only supply content to side prop, no top bar will be rendered for desktop screens.

For mobile screens the sidebar will be hidden and you will get a top bar with logo, app name and mobile menu button.

<Nav logo="Demo App" side={ <> <a href="#path"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="#path"> <Nav.Item icon={faEnvelopeOpenText}>Tickets</Nav.Item> </a> </> } > Your app content goes here </Nav>
<Nav logo="Demo App" side={ <> <a href="#path"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="#path"> <Nav.Item icon={faEnvelopeOpenText}>Tickets</Nav.Item> </a> </> } > Your app content goes here </Nav>
Skip to main content
Your app content goes here

Standalone top bar

<Nav logo="Demo App" top={ <> <a href="#path"> <Nav.Item>Home</Nav.Item> </a> <a href="#path" title="User profile"> <Nav.Item icon={faUser} /> </a> </> } > Your app content goes here </Nav>
<Nav logo="Demo App" top={ <> <a href="#path"> <Nav.Item>Home</Nav.Item> </a> <a href="#path" title="User profile"> <Nav.Item icon={faUser} /> </a> </> } > Your app content goes here </Nav>
Skip to main content
Your app content goes here

Grouped items

You can have grouped sections of links by wrapping them (or other content) in a <Nav.Group>. Avoid using icons on items inside a group.

  • Renders as a popup on hover (or enter/space for keyboard users) for top bar, and for sidebar when collapsed
  • Renders as a expandable submenu for mobile nav and sidebar when expanded
  • Will be marked as active if there are any active sublinks (.active class on a link or button inside the group). Can be overridden with the active prop.
<Nav.Group icon={faPencilRuler} name="Applications"> <a href="/overview"> <Nav.Item>Overview</Nav.Item> </a> <a href="/apps"> <Nav.Item>Applications</Nav.Item> </a> </Nav.Group>
<Nav.Group icon={faPencilRuler} name="Applications"> <a href="/overview"> <Nav.Item>Overview</Nav.Item> </a> <a href="/apps"> <Nav.Item>Applications</Nav.Item> </a> </Nav.Group>

Can be used inside <Nav side={...}> and will be rendered as an expandable section. When the sidebar is collapsed it will be rendered as a pop-out instead.

Skip to main content
Collapsed sidebar renders groups as pop-outs instead of expandable sections

Or inside <Nav top={...}>, will be rendered as a dropdown.

Skip to main content
Your app content goes here

Profile picture

To create a profile picture dropdown, use the .bf-nav-profile class on an <img>.

<Nav.Group name={<img className="bf-nav-profile" src="..." alt="Profile menu" />} > [dropdown content...] </Nav.Group>
<Nav.Group name={<img className="bf-nav-profile" src="..." alt="Profile menu" />} > [dropdown content...] </Nav.Group>
Skip to main content
[dropdown content...]

See full code and demo in the Profile Picture Example.

Search input

You can have a search input in either sidebar or top bar by using <Nav.Search>. It renders as an <input> and accepts the same value and onChange props.

Use the optional onSubmit for handling clicks on the search icon (or enter keypress).

<Nav.Search value={searchValue} onChange={(e) => setSearchValue(e.target.value)} onSubmit={() => alert("search for " + searchValue)} />
<Nav.Search value={searchValue} onChange={(e) => setSearchValue(e.target.value)} onSubmit={() => alert("search for " + searchValue)} />
Skip to main content
Collapsed sidebar renders groups as pop-outs instead of expandable sections

When used in sidebar, we recommend placing it as the first menu item. It will be rendered inside a dropout when sidebar is collapsed, and a simple input for mobile/tablet menu.

Skip to main content
Your app content goes here

useNav hook

Allows you to access internal state of the Nav, like sideCollapsed and mobileOpen.

See details and usage at useNav hook docs

Highlight active page

You can add an .active class to the <a> link (or <button>) to mark it as active for the current page.

<a href="/home" className="active"> <Nav.Item icon={faHome}>Home</Nav.Item> </a>
<a href="/home" className="active"> <Nav.Item icon={faHome}>Home</Nav.Item> </a>

Next.js App Router

When using Next.JS 13's App Router you can get the current path using the usePathname hook, then apply the .active class when appropriate.

// app/layout.tsx import { type PropsWithChildren } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import Nav from "@intility/bifrost-react/Nav"; import { faHome } from "@fortawesome/pro-regular-svg-icons"; export default function RootLayout({ children }: PropsWithChildren) { const pathname = usePathname(); return ( <html lang="en" className="bf-scrollbar"> <body> <Nav logo={ <Link href="/"> <Nav.Logo>Example app</Nav.Logo> </Link> } side={ <> <Link className={pathname === "/" ? "active" : ""} href="/"> <Nav.Item icon={faHome}>Home</Nav.Item> </Link> <Link className={pathname === "/profile" ? "active" : ""} href="/profile" > <Nav.Item icon={faUser}>Profile</Nav.Item> </Link> </> } > {children} </Nav> </body> </html> ); }
// app/layout.tsx import { type PropsWithChildren } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import Nav from "@intility/bifrost-react/Nav"; import { faHome } from "@fortawesome/pro-regular-svg-icons"; export default function RootLayout({ children }: PropsWithChildren) { const pathname = usePathname(); return ( <html lang="en" className="bf-scrollbar"> <body> <Nav logo={ <Link href="/"> <Nav.Logo>Example app</Nav.Logo> </Link> } side={ <> <Link className={pathname === "/" ? "active" : ""} href="/"> <Nav.Item icon={faHome}>Home</Nav.Item> </Link> <Link className={pathname === "/profile" ? "active" : ""} href="/profile" > <Nav.Item icon={faUser}>Profile</Nav.Item> </Link> </> } > {children} </Nav> </body> </html> ); }

React Router

When using <NavLink> from react-router-dom, an .active CSS class will automatically be added on the link, which is styled by bifrost.

import { Link, NavLink, BrowserRouter as Router, Routes, Route, } from "react-router-dom"; // v6 import { faHome, faUser, faTools } from "@fortawesome/pro-regular-svg-icons"; import Nav from "@intility/bifrost-react/Nav"; export default function MyApp() { return ( <Router> <Nav logo={ <Link to="/"> <Nav.Logo>Example app</Nav.Logo> </Link> } side={ <> <NavLink to="/"> <Nav.Item icon={faHome}>Home</Nav.Item> </NavLink> <NavLink to="/profile"> <Nav.Item icon={faUser}>Profile</Nav.Item> </NavLink> <NavLink to="/app"> <Nav.Item icon={faTools}>Applications</Nav.Item> </NavLink> </> } > <Routes> <Route path="/" element={<div>Home page</div>} /> <Route path="/profile" element={<div>profile page</div>} /> <Route path="/app" element={<div>Applications page</div>} /> </Routes> </Nav> </Router> ); }
import { Link, NavLink, BrowserRouter as Router, Routes, Route, } from "react-router-dom"; // v6 import { faHome, faUser, faTools } from "@fortawesome/pro-regular-svg-icons"; import Nav from "@intility/bifrost-react/Nav"; export default function MyApp() { return ( <Router> <Nav logo={ <Link to="/"> <Nav.Logo>Example app</Nav.Logo> </Link> } side={ <> <NavLink to="/"> <Nav.Item icon={faHome}>Home</Nav.Item> </NavLink> <NavLink to="/profile"> <Nav.Item icon={faUser}>Profile</Nav.Item> </NavLink> <NavLink to="/app"> <Nav.Item icon={faTools}>Applications</Nav.Item> </NavLink> </> } > <Routes> <Route path="/" element={<div>Home page</div>} /> <Route path="/profile" element={<div>profile page</div>} /> <Route path="/app" element={<div>Applications page</div>} /> </Routes> </Nav> </Router> ); }

hideBranding

Since bifrost-react@4.2.0 we include "an intility service" at the bottom of sidebar. If you need to remove it for some reason, you can use the hideBranding prop.

Separate sub-components

Use in special cases only

For almost all use cases, you can safely stick to the full <Nav> component, which gives you a responsive menu out of the box.

The <Nav> component is built internally using CSS breakpoints and sub-components: <Nav.Side>, <Nav.Top>, and <Nav.Mobile>.

These components are basic HTML elements with Bifrost classnames applied. For advanced usage (for instance, sidebar only appears on certain pages) you can use these components yourself to compose whatever responsive navigation layout you want.

The useNav hook is tied to the <Nav> component and will not do anything when working with the sub-components separately.

Nav.Side and Nav.Top

If you use both Nav.Side and Nav.Top, the order is important. Put Nav.Side before Nav.Top (and <main> last), and supply logo only to Top.

LocalStorage/cookie state persistance is implemented in <Nav> component, when using <Nav.Side> directly you can persist its state yourself with collapsed and onCollapseChange props.

<Nav.Side logo='Demo App' > <a href='#path'> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href='#path'> <Nav.Item icon={faEnvelopeOpenText}>Tickets</Nav.Item> </a> </Nav.Side> <main>App content goes here</main>
<Nav.Side logo='Demo App' > <a href='#path'> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href='#path'> <Nav.Item icon={faEnvelopeOpenText}>Tickets</Nav.Item> </a> </Nav.Side> <main>App content goes here</main>
App content goes here
<Nav.Top logo='Demo App'> <a href='#path'> <Nav.Item>Home</Nav.Item> </a> <a href='#path' title='User profile'> <Nav.Item icon={faUser} /> </a> </Nav.Top> <main>App content goes here</main>
<Nav.Top logo='Demo App'> <a href='#path'> <Nav.Item>Home</Nav.Item> </a> <a href='#path' title='User profile'> <Nav.Item icon={faUser} /> </a> </Nav.Top> <main>App content goes here</main>
App content goes here

Basically a big, fixed element that takes up the whole screen (leaves space for top bar), suitable for mobile navigation popup. You will need to toggle it yourself when not using the full <Nav> component. Add an onOverlayClick callback to get an overlay over the page content.

<Nav.Mobile onOverlayClick={handleOverlayClick}> <a href="#path"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="#path"> <Nav.Item icon={faUser}>Profile</Nav.Item> </a> </Nav.Mobile>
<Nav.Mobile onOverlayClick={handleOverlayClick}> <a href="#path"> <Nav.Item icon={faHome}>Home</Nav.Item> </a> <a href="#path"> <Nav.Item icon={faUser}>Profile</Nav.Item> </a> </Nav.Mobile>

In order to open and close, you have to toggle between .bf-nav-mobile-open and .bf-nav-mobile-close classes on the <Nav.Mobile> element.

<Nav.Mobile className='bf-nav-mobile-open' /> <Nav.Mobile className='bf-nav-mobile-close' />
<Nav.Mobile className='bf-nav-mobile-open' /> <Nav.Mobile className='bf-nav-mobile-close' />