Source Code: https://github.com/shaikathaque/navbar-responsive-shadcn-ui-example
Live demo: https://navbar-responsive-shadcn-ui-example.netlify.app/
Overview
While there are many difference ways to build a responsive navbar in React, this article will utilize Tailwind CSS
for shadcn/ui
.
This post is influenced by the responsive navbar in shadcn/ui’s website, which is open source. Shadcn/ui has become a very popular choice in the React ecosystem for building UIs that are beautiful, accessible, and customizable.
In order to emphasize the core parts of the the navbar, there will be less focus on styling and advanced functionality.
Key Components
- Site Header component, which holds both mobile and main navbar components.
- Main navbar component, visible on medium screen sizes and above.
- Mobile navbar component, visible on small screen sizes, and toggles open a sheet as a menu.
1. Site Header Component
Add a SiteHeader
component in the root of our app, in this case, App.tsx
. The SiteHeader
component will contain both our main and and mobile navbar components.
// App.tsx
import SiteHeader from '@/components/siteHeader';
function App() {
return (
<div className="flex min-h-screen flex-col">
<SiteHeader />
{/* Body components go here */}
</div>
);
}
export default App;
In the SiteHeader.tsx
, we can include both the mobile and desktop nav components. Within each of those, we will then add tailwind-css
classes to control their visibility.
// SiteHeader.tsx
import MainNav from './mainNav';
import MobileNav from './mobileNav';
export default function SiteHeader() {
return (
<header className="w-full border-b">
<div className="flex h-14 items-center px-4">
<MainNav />
<MobileNav />
</div>
</header>
);
}
2. Main Nav Component
The MainNav
component is what will be visible in medium screen sizes and above.
// MainNav.tsx
import { Button } from './ui/button';
const mainNavItems = ['A', 'B', 'C'];
export default function MainNav() {
return (
<div className="mr-4 hidden gap-2 md:flex">
{mainNavItems.map((item, index) => (
<Button key={index} variant="link">
{item}
</Button>
))}
</div>
);
}
There are 2 key classes that help achieve this: hidden
and md:flex
.
The hidden
sets display: none
to the div by default. However, when the screen size is medium (768 pixels) or above, md:flex
sets display: flex
, making the div
visible.
3. Mobile Nav Component
The MobileNav
component will only be visible in screen sizes smaller than medium, in contrast to the MainNav
which is visible in medium screens and larger.
// MobileNav.tsx
import { useState } from 'react';
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
import { Button } from '@/components/ui/button';
import { Menu as MenuIcon } from 'lucide-react';
const mobileItems = ['A', 'B', 'C'];
export default function MobileNav() {
const [open, setOpen] = useState(false);
return (
<Sheet open={open} onOpenChange={setOpen}>
{/* This button will trigger open the mobile sheet menu */}
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="md:hidden">
<MenuIcon />
</Button>
</SheetTrigger>
<SheetContent side="left">
<div className="flex flex-col items-start">
{mobileItems.map((item, index) => (
<Button
key={index}
variant="link"
onClick={() => {
setOpen(false);
}}
>
{item}
</Button>
))}
</div>
</SheetContent>
</Sheet>
);
}
The key class here is the md:hidden
on the Button
component within the SheetTrigger
.
This makes the menu trigger button visible by default, but will have display: none
when the screen size is md (768 pixels)
and larger.
The SheetContent
component’s side
prop allows you to choose which direction the sheet will open from.
Parting Thoughts
The React ecosystem is changing really quickly. A couple years from now, there might be a different recommended way to build a mobile navbar. However, the fundamentals and underlying concepts stay the same.
If you have any feedback about this post or if anything is unclear, please reach out to me and I’ll do my best to help.
Top comments (2)
Thank you
Great work mate!