Responsive Navbar in React using shadcn/ui and Tailwind CSS
Tags: react, webdev, tutorial, javascript
Published: 2024-03-20 09:45:09

*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](https://ui.shadcn.com/), which is [open source](https://github.com/shadcn-ui/ui/blob/main/apps/www/components/mobile-nav.tsx). 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

1. Site Header component, which holds both mobile and main navbar components.
2. Main navbar component, visible on medium screen sizes and above.
3. 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.

```tsx
// 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.

```tsx
// 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.

```tsx
// 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>
  );
}
```

![Main Navbar on large screen size](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjnc9s4q8dqu1zfcartq.png)



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.

```tsx
// 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>
  );
}
```


![Mobile Navbar on small screen size](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8094kl4knr40cn23s3i.png)


![Mobile menu opened](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0c7ct2648spwr6836ydn.png)




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.