Muhi Logo Text
AboutBlogWork With Me

React Responsive Navbar with MUI

Creating and customizing a responsive navbar that adapts on mobile and desktop devices using Material UI (MUI) & React.

Last updated on July 12, 2024

react
mui
Responsive Navbar with Material UI (MUI)

Creating a responsive header for mobile and other devices is essential to accessing a wider audience. Users increasingly access websites from various devices with different screen sizes, so a responsive navbar ensures a seamless and user-friendly experience.

In this tutorial, we will learn how to create vertical tabs in React and Material UI. MUI provides a robust set of components and styling options that make the implementation of vertical tabs straightforward and highly customizable.

Creating a Basic Navbar

To get started, we need to install MUI dependencies in our React project:

npm install @mui/material @emotion/react @emotion/styled
npm install @mui/icons-material

Let’s create a simple header with a logo on the left and navigation links on the right. The links will be defined in an array that we can loop through to populate the navbar, making it easy to add or remove items:

import React from "react";
import {
  Stack,
  Link,
  Toolbar,
  Typography,
  Container,
  AppBar,
} from "@mui/material";

const pages = [
  { name: "Products", id: "products" },
  { name: "Services", id: "services" },
  { name: "About", id: "about" },
  { name: "Testimonials", id: "testimonials" },
  { name: "Contact", id: "contact" },
];

const Header = () => {
  return (
    <AppBar>
      <Container>
        <Toolbar>
          <Stack
            direction="row"
            justifyContent="space-between"
            alignItems="center"
            width="100%"
          >
            <Typography variant="h6">My App</Typography>
            <Stack direction="row" gap={3}>
              {pages.map(page => (
                <Link
                  key={page.id}
                  sx={{
                    color: { xs: "primary", sm: "white" },
                  }}
                >
                  {page.name}
                </Link>
              ))}
            </Stack>
          </Stack>
        </Toolbar>
      </Container>
    </AppBar>
  );
};

export default Header;

In the code above, we created a Header component that renders the following components from MUI:

  • AppBar: Creates an application bar that holds the navigation elements.
  • Container: Centers the content horizontally with padding on the sides.
  • Toolbar: Aligns its children horizontally, providing a flexible container within the AppBar.
  • Stack: Arranges the logo and navigation links in a row, with the logo on the left and links on the right.

Responsive navbar with a drawer

There are multiple ways to create responsive layouts in MUI, but in this tutorial, we’ll use the sx prop to handle the changes. For a more detailed understanding, you can refer to the tutorial on Breakpoints and Responsive Design.

Let’s walk through how to make a responsive navbar using a drawer for mobile devices.

Simple Example to Show/Hide on Mobile

First, we’ll demonstrate how to show and hide elements based on the screen size using the sx prop:

import { Box, Typography } from "@mui/material";

const ResponsiveExample = () => {
  return (
    <Box>
      <Typography sx={{ display: { xs: "none", sm: "block" } }}>
        Other Devices
      </Typography>
      <Typography sx={{ display: { xs: "block", sm: "none" } }}>
        Mobile Device
      </Typography>
    </Box>
  );
};

export default ResponsiveExample;

The breakpoint xs applies to extra-small screens (mobile) up to sm screens (small devices). Meanwhile, sm applies to small devices and above because there is no breakpoint after sm, so it will be applied to all other screens.

We can try to resize the window using the handle at the bottom right corner to see the text changing based on the screen size.

We’ll start by creating a NavList component that will host the links and be responsible for updating the links’ direction, spacing, and other styling based on the breakpoint/device size:

const NavList = ({ ...props }) => {
  return (
    <Stack
      overflow="auto"
      direction={{ xs: "column", sm: "row" }}
      gap={3}
      ml={{ xs: 3, sm: 0 }}
      mt={{ xs: 3, sm: 0 }}
      width={{ xs: "150px", sm: "initial" }}
      {...props}
    >
      {pages.map(page => (
        <Link
          key={page.id}
          sx={{
            color: { xs: "primary", sm: "white" },
          }}
        >
          {page.name}
        </Link>
      ))}
    </Stack>
  );
};

The Stack component arranges the links in a column on mobile devices and a row on larger screens. We also set the margin and width based on the screen size to ensure proper spacing and alignment of the links.

The Nav component will contain the menu button icon and Drawer for mobile devices. The NavList will be used within the Drawer component for mobile and displayed directly for larger screens:

const Nav = () => {
  const [open, setOpen] = useState(false);
  const toggleDrawer = newOpen => () => {
    setOpen(newOpen);
  };

  return (
    <>
      <Button
        variant="text"
        onClick={toggleDrawer(true)}
        sx={{ color: "white", display: { xs: "flex", sm: "none" } }}
      >
        <MenuIcon />
      </Button>
      <Drawer
        open={open}
        onClose={toggleDrawer(false)}
        anchor="right"
        sx={{
          display: { xs: "inherit", sm: "none" },
        }}
      >
        <NavList />
      </Drawer>
      <NavList
        sx={{
          display: { xs: "none", sm: "inherit" },
        }}
      />
    </>
  );
};

The Drawer component creates a sliding menu that opens from the right side of the screen on mobile devices. The open state and toggleDrawer function are used to control the drawer’s visibility.

Complete Header Component

Now, we’ll put everything together by replacing the initial links we had in the header with the new Nav component:

import React, { useState } from "react";
import {
  Stack,
  Link,
  Toolbar,
  Typography,
  Container,
  AppBar,
  Button,
  Drawer,
} from "@mui/material";
import MenuIcon from "@mui/icons-material/Menu";

const pages = [
  { name: "Products", id: "products" },
  { name: "Services", id: "services" },
  { name: "About", id: "about" },
  { name: "Testimonials", id: "testimonials" },
  { name: "Contact", id: "contact" },
];

const NavList = ({ ...props }) => {
  return (
    <Stack
      overflow="auto"
      direction={{ xs: "column", sm: "row" }}
      gap={3}
      ml={{ xs: 3, sm: 0 }}
      mt={{ xs: 3, sm: 0 }}
      width={{ xs: "150px", sm: "initial" }}
      {...props}
    >
      {pages.map(page => (
        <Link
          key={page.id}
          sx={{
            color: { xs: "primary", sm: "white" },
          }}
        >
          {page.name}
        </Link>
      ))}
    </Stack>
  );
};

const Nav = () => {
  const [open, setOpen] = useState(false);
  const toggleDrawer = newOpen => () => {
    setOpen(newOpen);
  };

  return (
    <>
      <Button
        variant="text"
        onClick={toggleDrawer(true)}
        sx={{ color: "white", display: { xs: "flex", sm: "none" } }}
      >
        <MenuIcon />
      </Button>
      <Drawer
        open={open}
        onClose={toggleDrawer(false)}
        anchor="right"
        sx={{
          display: { xs: "inherit", sm: "none" },
        }}
      >
        <NavList />
      </Drawer>
      <NavList
        sx={{
          display: { xs: "none", sm: "inherit" },
        }}
      />
    </>
  );
};

const Header = () => {
  return (
    <AppBar>
      <Container>
        <Toolbar>
          <Stack
            direction="row"
            justifyContent="space-between"
            alignItems="center"
            width="100%"
          >
            <Typography variant="h6">My App</Typography>
            <Nav />
          </Stack>
        </Toolbar>
      </Container>
    </AppBar>
  );
};

export default Header;

When resizing the window in the above code example, the navbar changes its layout based on the screen size, and the navigation links are displayed in a drawer on mobile devices.

Mobile Menu Customization

MUI drawers allow us to change the position of the sliding menu using the anchor prop. Here’s an example of making the menu slide from the top:

<Drawer open={open} onClose={toggleDrawer(false)} anchor="top">
  <NavList />
</Drawer>

The options for the anchor prop are top, bottom, left, and right. For more details on the Drawer component API, you can refer to the MUI Drawer documentation.

Customizing the Mobile Menu to Slide Full Screen

To make the menu appear friendlier on mobile devices, we can make it slide in as a full screen and tweak the drawer’s style. We’ll also add a handy “X” icon at the top right corner for users to easily close the menu, especially since clicking outside won’t work when the whole screen is covered.

import React, { useState } from "react";

import {
  Stack,
  Link,
  Toolbar,
  Typography,
  Container,
  AppBar,
  Button,
  Drawer,
  Box,
} from "@mui/material";

import MenuIcon from "@mui/icons-material/Menu";
import CloseIcon from "@mui/icons-material/Close";

const pages = [
  { name: "Products", id: "products" },
  { name: "Services", id: "services" },
  { name: "About", id: "about" },
  { name: "Testimonials", id: "testimonials" },
  { name: "Contact", id: "contact" },
];

const Nav = () => {
  const [open, setOpen] = useState(false);
  const toggleDrawer = newOpen => () => {
    setOpen(newOpen);
  };

  return (
    <>
      <Button
        variant="text"
        onClick={toggleDrawer(true)}
        sx={{ color: "white", display: { xs: "flex", sm: "none" } }}
      >
        <MenuIcon />
      </Button>
      <Drawer
        open={open}
        onClose={toggleDrawer(false)}
        anchor="right"
        sx={{
          display: { xs: "inherit", sm: "none" },
          "& .MuiDrawer-paper": {
            height: "100%",
            width: "100%",
          },
        }}
      >
        <Box
          sx={{
            display: "flex",
            justifyContent: "flex-end",
            p: 2,
          }}
        >
          <Button onClick={toggleDrawer(false)}>
            <CloseIcon />
          </Button>
        </Box>
        <NavList />
      </Drawer>
      <NavList
        sx={{
          display: { xs: "none", sm: "inherit" },
        }}
      />
    </>
  );
};

const NavList = ({ ...props }) => {
  return (
    <Stack
      overflow="auto"
      direction={{ xs: "column", sm: "row" }}
      gap={3}
      width={{ xs: "100%", sm: "initial" }}
      textAlign={{ xs: "center", sm: "initial" }}
      fontSize={{ xs: "22px", sm: "initial" }}
      {...props}
    >
      {pages.map(page => (
        <Link
          key={page.id}
          sx={{
            color: { xs: "primary", sm: "white" },
            textDecoration: "none",
          }}
        >
          {page.name}
        </Link>
      ))}
    </Stack>
  );
};

const Header = () => {
  return (
    <AppBar>
      <Container>
        <Toolbar>
          <Stack
            direction="row"
            justifyContent="space-between"
            alignItems="center"
            width="100%"
          >
            <Typography variant="h6">My App</Typography>
            <Nav />
          </Stack>
        </Toolbar>
      </Container>
    </AppBar>
  );
};
export default Header;

In the code above, we added a CloseIcon to the drawer and customized its styling to cover the entire screen using the .MuiDrawer-paper class. The NavList component was also updated to adjust the text alignment, font size, and width based on the screen size.

Summary

In this tutorial, we created a responsive navbar using React and Material UI (MUI). Initially, we designed a basic navbar with a logo and navigation links. Then, we made the navbar responsive and added a drawer for better mobile experience. The result is a dynamic and user-friendly navbar for desktop and mobile devices.

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to create encapsulated and reusable Fieldset component with Material UI (MUI) and React.

react
mui

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri