DEV Community

Cover image for Chakra-UI Responsive Navigation Bar
Benjamin Carlson
Benjamin Carlson

Posted on • Updated on • Originally published at coffeeclass.io

Chakra-UI Responsive Navigation Bar

Introduction

If you are familiar with Bootstrap, you know how easy it is to create a responsive nav-bar. In Chakra-UI, there is no built in, out of the box solution. In this snippet, we will create a responsive nav-bar component that you can use in your React app.

Setup

We will be using Next.js in this example but it works for any react based framework. Navigate to the Next.js GitHub repo and clone their starter example using Chakra-UI. If you don't want to leave this page the command is:

yarn create next-app --example with-chakra-ui with-chakra-ui-app

Open this in your preferred IDE - mine is VSCode. Inside of src/components, open up the DarkModeSwitch component. This is the only file we will be modifying.

Imports

We will start by importing everything we need.

import { useState } from 'react'
import {
  useColorMode,
  Switch,
  Flex,
  Button,
  IconButton
} from '@chakra-ui/react'
import { HamburgerIcon, CloseIcon } from '@chakra-ui/icons'
import NextLink from 'next/link'
Enter fullscreen mode Exit fullscreen mode

Adding Desktop Content

First, wrap everything inside of a Flex element. Then, add the below code.

<Flex>
<Flex position="fixed" top="1rem" right="1rem" align="center">
  {/* Desktop */}
  <Flex>
    <NextLink href="/" passHref>
      <Button as="a" variant="ghost" aria-label="Home" my={5} w="100%">
        Home
      </Button>
    </NextLink>

    <NextLink href="/about" passHref>
      <Button as="a" variant="ghost" aria-label="About" my={5} w="100%">
        About
      </Button>
    </NextLink>

    <NextLink href="/contact" passHref>
      <Button as="a" variant="ghost" aria-label="Contact" my={5} w="100%">
        Contact
      </Button>
    </NextLink>
  </Flex>

  {/* Mobile */}
  <IconButton
    aria-label="Open Menu"
    size="lg"
    mr={2}
    icon={<HamburgerIcon />}
    onClick={}
  />
  <Switch color="green" isChecked={isDark} onChange={toggleColorMode} />
</Flex>
{/* Mobile Content */}
</Flex>
Enter fullscreen mode Exit fullscreen mode

Adding Mobile Content

This is simply our desktop nav bar. We will add the mobile content below the comment. Let's do that now.

{/* Code above */}

{/* Mobile Content */}
<Flex
  bgColor="gray.50"
  overflowY="auto"
  flexDir="column"
>
  <Flex justify="flex-end">
    <IconButton
      mt={2}
      mr={2}
      aria-label="Open Menu"
      size="lg"
      icon={<CloseIcon />}
      onClick={}
    />
  </Flex>

  <Flex flexDir="column" align="center">
    <NextLink href="/" passHref>
      <Button as="a" variant="ghost" aria-label="Home" my={5} w="100%">
        Home
      </Button>
    </NextLink>

    <NextLink href="/about" passHref>
      <Button as="a" variant="ghost" aria-label="About" my={5} w="100%">
        About
      </Button>
    </NextLink>

    <NextLink href="/contact" passHref>
      <Button as="a" variant="ghost" aria-label="Contact" my={5} w="100%">
        Contact
      </Button>
    </NextLink>
  </Flex>
</Flex>
Enter fullscreen mode Exit fullscreen mode

Using useState To Open And Close Navigation

Now that we have content, we need a way to show it. We can use useState for this. Before the return statement, add the following:

const [display, changeDisplay] = useState('none')
Enter fullscreen mode Exit fullscreen mode

We now have a variable display set initially to none, and a method changeDisplay we can use to change it.

Let's add this to our code. Note, I am only writing the components that we are changing below.

<IconButton
          aria-label="Open Menu"
          size="lg"
          mr={2}
          icon={
            <HamburgerIcon />
          }
          onClick={() => changeDisplay('flex')} // added line
        />

<Flex
  display={display} // added line
  bgColor="gray.50"
  overflowY="auto"
  flexDir="column"
>

<IconButton
            mt={2}
            mr={2}
            aria-label="Open Menu"
            size="lg"
            icon={
              <CloseIcon />
            }
            onClick={() => changeDisplay('none')} // added line
          />
Enter fullscreen mode Exit fullscreen mode

Now we should be able to open and close the menu! It looks a bit messy though. Let's add styles to the Flex.

<Flex
  w="100vw"
  display={display}
  bgColor="gray.50"
  zIndex={20}
  h="100vh"
  pos="fixed"
  top="0"
  left="0"
  overflowY="auto"
  flexDir="column"
>
Enter fullscreen mode Exit fullscreen mode

The important styles we added:

  • Setting the height to 100vh
  • Setting the width to 100vw
  • Setting the position to fixed
  • Making z-index 20 so it is above the page content
  • Setting top and left to 0
  • Setting the display to our dynamic display variable.

The rest is subjective.

Completed Code

And that's it! Here is the completed code:

import { useState } from 'react'
import {
  useColorMode,
  Switch,
  Flex,
  Button,
  IconButton
} from '@chakra-ui/react'
import { HamburgerIcon, CloseIcon } from '@chakra-ui/icons'
import NextLink from 'next/link'


export const DarkModeSwitch = () => {
  const { colorMode, toggleColorMode } = useColorMode()
  const isDark = colorMode === 'dark'
  const [display, changeDisplay] = useState('none')
  return (
    <Flex>
      <Flex
        position="fixed"
        top="1rem"
        right="1rem"
        align="center"
      >
        {/* Desktop */}
        <Flex
          display={['none', 'none', 'flex','flex']}
        >
          <NextLink href="/" passHref>
            <Button
              as="a"
              variant="ghost"
              aria-label="Home"
              my={5}
              w="100%"
            >
              Home
                    </Button>
          </NextLink>

          <NextLink href="/about" passHref>
            <Button
              as="a"
              variant="ghost"
              aria-label="About"
              my={5}
              w="100%"
            >
              About
                    </Button>
          </NextLink>

          <NextLink href="/contact" passHref>
            <Button
              as="a"
              variant="ghost"
              aria-label="Contact"
              my={5}
              w="100%"
            >
              Contact
                    </Button>
          </NextLink>
        </Flex>

        {/* Mobile */}
        <IconButton
          aria-label="Open Menu"
          size="lg"
          mr={2}
          icon={
            <HamburgerIcon />
          }
          onClick={() => changeDisplay('flex')}
          display={['flex', 'flex', 'none', 'none']}
        />
        <Switch
          color="green"
          isChecked={isDark}
          onChange={toggleColorMode}
        />
      </Flex>

      {/* Mobile Content */}
      <Flex
        w='100vw'
        display={display}
        bgColor="gray.50"
        zIndex={20}
        h="100vh"
        pos="fixed"
        top="0"
        left="0"
        zIndex={20}
        overflowY="auto"
        flexDir="column"
      >
        <Flex justify="flex-end">
          <IconButton
            mt={2}
            mr={2}
            aria-label="Open Menu"
            size="lg"
            icon={
              <CloseIcon />
            }
            onClick={() => changeDisplay('none')}
          />
        </Flex>

        <Flex
          flexDir="column"
          align="center"
        >
          <NextLink href="/" passHref>
            <Button
              as="a"
              variant="ghost"
              aria-label="Home"
              my={5}
              w="100%"
            >
              Home
                    </Button>
          </NextLink>

          <NextLink href="/about" passHref>
            <Button
              as="a"
              variant="ghost"
              aria-label="About"
              my={5}
              w="100%"
            >
              About
                    </Button>
          </NextLink>

          <NextLink href="/contact" passHref>
            <Button
              as="a"
              variant="ghost"
              aria-label="Contact"
              my={5}
              w="100%"
            >
              Contact
            </Button>
          </NextLink>
        </Flex>
      </Flex>
    </Flex>
  )
}
Enter fullscreen mode Exit fullscreen mode

Discussion (2)

Collapse
merthod profile image
Merthod

Two questions,

  1. Isn't using useDisclosure a better fit than using state to open/close?
  2. In the mobile layout, what is the difference between using Flex and VStack in the outmost tag?

Thanks for the example!

Collapse
primozrome profile image
Primož Rome

Live preview would be nice?