Build accessible and functional Sidenav with Chakra UI
Tags: react, typescript, a11y, ui
Published: 2023-05-29 13:55:21

## Overview
Every web app on the internet has some kind of navigation that allows users to quickly navigate through different pages. Some web apps have navigation on the top and some have navigation on the side. In this blog, we will cover how to build a responsive and accessible sidenav. Building a responsive and accessible sidenav for your web app is quite challenging. There are various things that we need to consider while developing it. For example, In mobile devices where space is limited, a sidenav should be replaced with a [drawer](https://chakra-ui.com/docs/components/drawer).

From the [Accessibility(a11y)](https://www.w3.org/standards/webdesign/accessibility) point of view, if we are navigating via keyboard(`Tab` key) through nav items, then the order of items should be followed. If we are using icon buttons instead of text for the nav items, then each button should have a proper [aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) to define the purpose of the icon button.

In this blog, we will work on building functional and accessible sidenav. Along the sidenav, we will also focus on creating a reusable responsive layout with well-defined HTML regions to improve accessibility (a11y).

## Prerequisite
In order to get most of this blog, I will assume you have some knowledge about [React](https://react.dev/), [chakra ui](https://chakra-ui.com/), and [typescript](https://www.typescriptlang.org/). We will use `typescript` instead of `javascript` to build our sidenav with type safety. This will help us to find the bugs at the compile time. Nowadays, every framework support typescript. In addition to these, we will also use [react-router-dom](https://reactrouter.com/en/main) and [react-icons](https://react-icons.github.io/react-icons). I am also assuming you already have a React project created. There are a bunch of options to create. You can choose your favorite framework for setup. If you are ready, then let’s start constructing our sidenav.

## Understanding the Role of Sidenav Within the App Layout
A sidenav is a navigation panel in your web app that provides links to navigate through different routes. It is provided in the root level component along with the main content.

In the image below, a typical layout of a web app is shown. A web app can have a header, sidenav, main content, and footer.

To make them accessible, they should be defined with appropriate [landmarks](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/HTML5.html). With the right landmarks, a [screen reader](https://www.nvaccess.org/)(*assistive technologies that audibly convey the content of a computer screen to visually impaired users*) can easily switch between different regions of the web app. By adhering to HTML semantics, we can effectively establish meaningful landmarks. In HTML 5, some elements have pre-defined landmarks for different regions. A [header](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/banner.html) has a banner landmark, [an aside](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/complementary.html) element has a complimentary landmark, the [main](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/main.html) has a primary content landmark, and the [footer](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/contentinfo.html) has a content info landmark. You can read more about it on [W3C patterns](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/general-principles.html).

![Layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hw83odtelm0og8xma6um.png)

A sidenav is a complementary landmark. The purpose of the complementary landmarks is to provide a supporting section to the primary content. The navigation links are not the primary content of our web app but are important for the user to navigate through primary content. They should be on the same level as primary content. So, in order to provide the supporting content such as navigation links, we need sidenav.

## Why is Accessibility (a11y) Important to Us?
A11y(Accessibility) or digital a11y is a way to make your web app available for everyone. With everyone, we mean people with disabilities as well. According to WHO, **“_About 15% of the world's population lives with some form of disability, of whom 2-4% experience significant difficulties in functioning_”**. By default, the web/internet is designed to make it accessible to all kinds of people. That’s why there are different HTML elements and aria attributes exist. An accessible web app can reach a wider range of people and may result in more visitors to your app. There are various types of disabilities that can be covered by an accessible web app. You can learn more about it [here](https://web.dev/learn/accessibility/why/#what-is-the-individual-impact).

![kind of disabilities](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gz1hqyg2cr9g1v1sc8mw.png)

Screen readers are the most useful tools for quickly navigating through web apps for disabled people. A screen reader can understand the semantics of the HTML elements that are used. That’s why we should use the correct HTML elements instead of `div` or `span` everywhere. Not only the HTML elements, but the order of the elements also matters a lot. The order in which the elements are laid out will define which text will be read out first and which one after that. For example, in the `DOM`, we have two elements, and with `CSS` we change the order(by position absolute or fixed). The order is changed on the screen but for the screen readers, it’s not changed. So, it’s our responsibility to let the screen reader devices know about this.

The keyboard also plays an important role while handling a11y. As we know the mobility-impaired person might not be able to use the mouse or touch devices. In this case, a keyboard-accessible app makes it easy to use for such kinds of people.

## A Brief Introduction to Chakra UI
As per docs, "*_Chakra UI is a simple, modular, and accessible component library that gives you the building blocks you need to build your React applications._*" It is built on top of the [emotion](https://emotion.sh/docs/introduction) package, which provides a different pattern of writing CSS in JavaScript. Let’s start with installing the chakra ui package,
```tsx
npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
```
Additionally, we also need `react-router-dom` and `react-icons`. Let’s install it you haven’t installed it,

```tsx
npm install react-icons react-router-dom --save
```

## Folder Structure
```
.
└── sidenav/
├── sidenav-container/
│   ├── sidenav-container.tsx
│   └── sidenav-container.spec.tsx
├── sidenav-items/
│   ├── sidenav-items.tsx
│   └── sidenav-items.spec.tsx
├── sidenav-context/
│   ├── sidenav-context.tsx
│   └── sidenav-context.spec.tsx
├── sidenav.tsx
└── sidenav.spec.tsx
```
We have a ***sidenav*** folder within our ***components*** directory. The following components need to be created:-

1. `<Sidenav />` - Our sidenav which is visible by default on the large screen and convert into the drawer in mobile devices.
2. `<SidenavContainer />` - A container where our sidenav will be rendered along with the main content.
3. `<SidenavItems />` - List of all the nav items in the sidenav.
4. `<SidnavContext />` - Context to provide the props that can be shared in nested components. This will be used in case of the mobile devices where we have use drawer and the drawer can be opened from any place.

## Create a `<SidenavItems />` component
Let’s create our first component. Create a `sidenav/sidenav-items/sidenav-items.tsx` file. Inside the file, we will first create an interface for our `props`:

```tsx
import { IconType } from "react-icons";

export interface SidenavItem {
  icon: IconType;
  label: string;
  to: string;
}

export interface SidenavItemsProps {
  navItems: SidenavItem[];
  mode?: "semi" | "over";
}
```
Our `SidenavItemsProps` has two properties:

1. `navItems`: List of items of type `SidenavItem`. `SidenavItem` defines the structure of the single nav item that can be passed. It has `icon`, `label`and `to`(route URL). 
2. `mode`: Used to decide how nav items should be rendered in different devices. `semi` mode is set when opened on large screens and `over` is used when opened on mobile devices. This is an optional prop and the default value for this will be `semi`.

We have our `props` in place. Let's create our `<SidenavItems />` component for `semi` mode.

```tsx
import { List, ListItem, Icon, Flex, Text, Link, Tooltip, IconButton } from "@chakra-ui/react";
import { NavLink } from "react-router-dom";

export function SidenavItems({ navItems, mode = "semi" }: SidenavItemsProps) {
  const sidebarItemInSemiMode = (
    { icon: Icon, ...item }: SidenavItem,
    index: number
  ) => (
    <ListItem key={index}>
      <Tooltip label={item.label} placement="right">
        <IconButton
          key={index}
          as={NavLink}
          _focus={{ bg: "gray.100" }}
          _activeLink={{ boxShadow: "md", bg: "orange.500", color: "white" }}
          bg="transparent"
          aria-label={item.label}
          borderRadius="xl"
          icon={<Icon />}
          to={item.to}
        />
      </Tooltip>
    </ListItem>
  );
  return (
    <List spacing={3}>
        {navItems.map((item, index) => sidebarItemInSemiMode(item, index))}
    </List>
  );
}

export default SidenavItems;
```
We have `sidebarItemInSemiMode` in our `SidenavItems` component. This will render a single nav item inside our `SidenavItems` component. Since our nav items are a kind of list,  `List` component from chakra-ui would be appropriate for this. We need to wrap each item in `ListItem` component. Then we have `Tooltip` component wrapping the `IconButton` component. Sometimes understanding the purpose of the nav item is not clear by just looking at the icon. In such cases, giving a tooltip will make it easier for the users to understand by providing additional information.

The nav item should be a `link` that navigates to the provided route. But at the same time, it is also an `icon`. The `IconButton` component seems like the correct component for this. But from the a11y point of view, it should be a link(anchor tag). How can we combine both? Well, it’s not difficult. Charka-ui provides us `as` prop which can be used to specify the `ReactElement` that can be rendered but with the same styles. We will use the [NavLink](https://reactrouter.com/en/main/components/nav-link#navlink) component from `react-router-dom` to render the `IconButton` as a `link`. We also need to highlight our nav item whenever the user either `hovers` or `focuses` on it. On click, it should be marked as active in order to make the current nav item highlighted. This will help us to convey the right state of the link. The `_activeLink` and `_focus` props are used for this. The `hover` state is already handled by `IconButton`.  Note that the `active` state of the link is provided by `NavLink`. So in order to `_activeLink` to work, this is required.

Chakra ui provides us with a set of default [color palettes](https://chakra-ui.com/docs/styled-system/theme#colors). I am using orange here. But you can choose anyone.

`aria-label` is also provided to help screen readers to read the appropriate text for visually impaired users.

In the end, we need to iterate over the nav items and render them using the array `map` function.

Next, let’s create sidenav items for `over` mode.
```tsx
import { List, ListItem, Icon, Flex, Text, Link, Tooltip, IconButton } from "@chakra-ui/react";
import { NavLink } from "react-router-dom";

export function SidenavItems({ navItems, mode = "semi" }: SidenavItemsProps) {
  const sidebarItemInOverMode = (item: SidenavItem, index: number) => (
    <ListItem key={index}>
      <Link
        display="block"
        as={NavLink}
        to={item.to}
        _focus={{ bg: "gray.100" }}
        _hover={{
          bg: "gray.200"
        }}
        _activeLink={{ bg: "orange.500", color: "white" }}
        w="full"
        borderRadius="md"
      >
        <Flex alignItems="center" p={2}>
          <Icon boxSize="5" as={item.icon} />
          <Text ml={2}>{item.label}</Text>
        </Flex>
      </Link>
    </ListItem>
  );
  return (
    <List spacing={3}>
        {navItems.map((item, index) => sidebarItemInSemiMode(item, index))}
    </List>
  );
}

export default SidenavItems;
```

`sidebarItemInOverMode` is similar to `sidebarItemInSemiMode` . The only difference is instead of `IconButton` we are using `Link` component. In over mode, we are rendering the icon along with the text. So it is easy to understand the purpose of the icon. Hence, we don’t need to wrap it inside the tooltip. So the full component will look like this,

```tsx
import {
  List,
  ListItem,
  Icon,
  Flex,
  Text,
  Link,
  Tooltip,
  IconButton
} from "@chakra-ui/react";
import { IconType } from "react-icons";
import { NavLink } from "react-router-dom";

export interface SidenavItem {
  icon: IconType;
  label: string;
  to: string;
}

export interface SidenavItemsProps {
  navItems: SidenavItem[];
  mode?: "semi" | "over";
}

export function SidenavItems({ navItems, mode = "semi" }: SidenavItemsProps) {
  const sidebarItemInOverMode = (item: SidenavItem, index: number) => (
    <ListItem key={index}>
      <Link
        display="block"
        as={NavLink}
        to={item.to}
        _focus={{ bg: "gray.100" }}
        _hover={{
          bg: "gray.200"
        }}
        _activeLink={{ bg: "orange.500", color: "white" }}
        w="full"
        borderRadius="md"
      >
        <Flex alignItems="center" p={2}>
          <Icon boxSize="5" as={item.icon} />
          <Text ml={2}>{item.label}</Text>
        </Flex>
      </Link>
    </ListItem>
  );
  const sidebarItemInSemiMode = (
    { icon: Icon, ...item }: SidenavItem,
    index: number
  ) => (
    <ListItem key={index}>
      <Tooltip label={item.label} placement="right">
        <IconButton
          key={index}
          as={NavLink}
          _focus={{ bg: "gray.100" }}
          _activeLink={{ boxShadow: "md", bg: "orange.500", color: "white" }}
          bg="transparent"
          aria-label={item.label}
          borderRadius="xl"
          icon={<Icon />}
          to={item.to}
        />
      </Tooltip>
    </ListItem>
  );
  return (
    <List spacing={3}>
      {mode === "semi"
        ? navItems.map((item, index) => sidebarItemInSemiMode(item, index))
        : navItems.map((item, index) => sidebarItemInOverMode(item, index))}
    </List>
  );
}

export default SidenavItems;
```

While iterating over the nav items, we can check the mode provided to the <SidenavItems /> and render them accordingly. The preview of this component will looks like below,

![Sidenav items demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kg7sh69ie4ivxc3ik1fc.png)

## Sidenav context

### Why do we need sidenav context?

We are creating a responsive sidenav. As we mentioned earlier, for mobile devices we will use the [`<Drawer />`](https://chakra-ui.com/docs/components/drawer) component. The drawer will be hidden by default and can be opened by some action(button `click` etc). On some action, we need to convey this message to our drawer. This can be done by passing a callback as a prop to the sidenav. This is fine if you have one level of nesting for your component that will trigger the action. But if you have a deeply nested component that will trigger the action, this might be a tedious task. As per react docs,

“*Context provides a way to pass data through the component tree without having to pass props down manually at every level.”*

Our sidenav component is a layout component and will be added at the root level. Other components may be placed within routes or deeply nested. To enable communication between these components, we utilize sidenav `context`. This type of scenario commonly arises during application development. Alternatively, you could use simple `props`, but that would lead to [prop drilling](https://kentcdodds.com/blog/prop-drilling).

### Create a SidenavContext

Create a `sidenav/sidenav-context.tsx` file. We will add context inside the file:

```tsx
import { useDisclosure } from "@chakra-ui/react";
import { createContext } from "react";
const SidenavContext = createContext<ReturnType<typeof useDisclosure> | null>(
  null
);
```
Our sidenav requires specific props like `open`, `close`, `toggle`, etc. to manage the behavior of the `drawer`. Chakra ui provides a custom hook [useDisclosure](https://chakra-ui.com/docs/hooks/use-disclosure), designed for such components. Additionally, it is important to have a strongly typed context. To achieve this, we will utilize the [ReturnType](https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype) utility from TypeScript to obtain the functions and properties exposed by `useDisclosure`. Initially, the context will have no value, so we will initialize it with `null`.

Now, let’s create a custom hook for our sidenav context.

```tsx
import { useContext } from "react";

export function useSidenav() {
  const sidebar = useContext(SidenavContext);
  if (!sidebar) {
    throw new Error("Cannot use `sidebar context` outside SidebarProvider");
  }
  return { ...(sidebar as ReturnType<typeof useDisclosure>) };
}
```
Wrapping our context in the custom hook makes it easy to use in other places. Our sidenav context initially has a `null` value. So, it’s good to check if the context value is provided or not. If not, then simply throw an error.

Let’s create a provider,

```tsx
import { useDisclosure } from "@chakra-ui/react";

export function SidenavProvider({
  children,
  ...props
}: {
  children: React.ReactNode;
}) {
  const disclosure = useDisclosure();
  return (
    <SidenavContext.Provider value={{ ...disclosure }} {...props}>
      {children}
    </SidenavContext.Provider>
  );
}

export default SidenavProvider;
```

This code is wrapping the `SidenavContext.Provider` with `disclosure` value and exports it. This is a common practice in React and also makes the code easy to test. The whole code for sidenav context will look like this,

```tsx
import { useDisclosure } from "@chakra-ui/react";
import { createContext, useContext } from "react";

const SidenavContext = createContext<ReturnType<typeof useDisclosure> | null>(
  null
);

export function useSidenav() {
  const sidebar = useContext(SidenavContext);
  if (!sidebar) {
    throw new Error("Cannot use `sidebar context` outside SidebarProvider");
  }
  return { ...(sidebar as ReturnType<typeof useDisclosure>) };
}

export function SidenavProvider({
  children,
  ...props
}: {
  children: React.ReactNode;
}) {
  const disclosure = useDisclosure();
  return (
    <SidenavContext.Provider value={{ ...disclosure }} {...props}>
      {children}
    </SidenavContext.Provider>
  );
}

export default SidenavProvider;
```

## Create a `<Sidenav />` Component
Create a `sidenav/sidenav.tsx` file. Within the file, we will create components and interfaces for our `props`. 

```tsx
import React from "react";
import {
  Drawer,
  DrawerContent,
  DrawerCloseButton,
  DrawerHeader,
  DrawerOverlay,
  VStack,
  DrawerBody,
  Icon
} from "@chakra-ui/react";
import { SiWwise } from "react-icons/si";
import { useSidenav } from "./sidenav-context/sidenav-context";
import SidenavItems, { SidenavItem } from "./sidenav-items/sidenav-items";

export interface SidenavProps {
  navItems: SidenavItem[];
}

export function Sidenav({ navItems }: SidenavProps) {
  const { isOpen, onClose } = useSidenav();
  return (
    <React.Fragment>
      <VStack spacing="5" as="nav" display={{ base: "none", md: "flex" }}>
        <Icon as={SiWwise} boxSize={8} /> {/*OR PUT YOUR LOGO HERE */}
        <SidenavItems navItems={navItems} />
      </VStack>
      <Drawer placement="left" onClose={onClose} isOpen={isOpen}>
        <DrawerOverlay />
        <DrawerContent>
          <DrawerCloseButton />
          <DrawerHeader>Weather Wise</DrawerHeader>
          <DrawerBody>
            <SidenavItems navItems={navItems} mode="over" />
          </DrawerBody>
        </DrawerContent>
      </Drawer>
    </React.Fragment>
  );
}

export default Sidenav;
```

The `SidenavProps` interface is straightforward. It has a single property `navItems`. Let’s look at the `Sidenav` component. We are using our custom hook `useSidenav` coming from our `sidenav` context. After that, we have our JSX which contains two siblings that wrap our <SidenavItems /> component in different components. The first one is `VStack` and the other one is `Drawer`. Let’s look at the first one,

```tsx
<VStack spacing="5" as="nav" display={{ base: "none", md: "flex" }}>
  <Icon as={SiWwise} boxSize={8} /> {/*OR PUT YOUR LOGO HERE */}
  <SidenavItems navItems={navItems} />
</VStack>
```

 `VStack` will render the items vertically. Internally, it’s a [flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox) with a flex-direction `column`. As we discussed earlier, HTML semantics are crucial for accessibility (a11y). Therefore, we will once again use the `as` prop to render the `VStack` as a [nav](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav) element instead of a `div`. The `VStack` is visible on large screens but hidden on mobile devices. To achieve this, we use `display` prop with the value of `{{ base: "none", md: "flex" }}`. In Chakra UI, the breakpoints are designed with a mobile-first approach, so `base` refers to mobile devices. After the `VStack`, we have the logo (or in its absence, an Icon can be placed) and the SidenavItems.The SidenavItems component is set to `semi` mode by default, eliminating the need to provide it explicitly.

Now let's look at the other code snippet,

```tsx
<Drawer placement="left" onClose={onClose} isOpen={isOpen}>
    <DrawerOverlay />
    <DrawerContent>
      <DrawerCloseButton />
      <DrawerHeader>Sidenav Header</DrawerHeader>
      <DrawerBody>
        <SidenavItems navItems={navItems} mode="over" />
      </DrawerBody>
    </DrawerContent>
  </Drawer>
```
We are utilizing the `Drawer` component from Chakra UI, which represents a panel that slides out from the screen edge. The `Drawer` component requires an `onClose` callback, obtained from the `Sidenav` context. The `isOpen` prop determines whether the drawer is open or closed. By default, our `useSidenav` hook provides a `false` value for `isOpen`, resulting in the drawer being closed.  Our sidenav will always be rendered on the left side, we need to set the placement to `left`.

Next, we have the `DrawerOverlay` component, responsible for rendering the backdrop with a gray background. The `DrawerCloseButton` is an icon placed at the top to allow the closing of the drawer. The `DrawerHeader` contains the header text, wrapped inside a `header` tag.

Inside the `DrawerBody`, we have our `SidenavItems` component. This code snippet does not include breakpoints because our drawer is always closed by default. To show the drawer, we need to set `isOpen` to `true` through an external button that is visible only on mobile devices. It is worth noting that we are providing the `over` mode to the `SidenavItems` component, which renders the navigation items differently from the default `semi` mode.

The whole code for sidenav component will look like this,
```tsx
import React from "react";
import {
  Drawer,
  DrawerContent,
  DrawerCloseButton,
  DrawerHeader,
  DrawerOverlay,
  VStack,
  DrawerBody,
  Icon
} from "@chakra-ui/react";
import { SiWwise } from "react-icons/si";
import { useSidenav } from "./sidenav-context/sidenav-context";
import SidenavItems, { SidenavItem } from "./sidenav-items/sidenav-items";

export interface SidenavProps {
  navItems: SidenavItem[];
}

export function Sidenav({ navItems }: SidenavProps) {
  const { isOpen, onClose } = useSidenav();
  return (
    <React.Fragment>
      <VStack spacing="5" as="nav" display={{ base: "none", md: "flex" }}>
        <Icon as={SiWwise} boxSize={8} /> {/*OR PUT YOUR LOGO HERE */}
        <SidenavItems navItems={navItems} />
      </VStack>
      <Drawer placement="left" onClose={onClose} isOpen={isOpen}>
        <DrawerOverlay />
        <DrawerContent>
          <DrawerCloseButton />
          <DrawerHeader>Sidenav Header</DrawerHeader>
          <DrawerBody>
            <SidenavItems navItems={navItems} mode="over" />
          </DrawerBody>
        </DrawerContent>
      </Drawer>
    </React.Fragment>
  );
}

export default Sidenav;
```

## SidenavContainer

### Why do we need `<SidenavContainer />`?

With the above sidenav component, we have completed its implementation. You are free to use it as you see fit. However, from my perspective, it is only partially implemented. If I were to utilize it in an application, I would likely create a layout component and incorporate our sidenav within it. The sidenav is an integral part of our layout, and layout components bring together all the core components that remain unchanged regardless of the routes. If you wish to take it a step further, we can begin by creating our `<SidenavContainer />` component.

### Create `<SidenavContainer />` component

Create a `sidenav/sidenav-container.tsx` file. Within the file, we will create an interface for our `props`. 

```tsx
import { ReactNode, ReactElement } from "react";

export interface SidenavContainerProps {
  children: ReactNode;
  sidenav: ReactElement;
}
```
There are two props for our `SidenavContainer` component: `children` and `sidenav`. `Children` are the react nodes that are passed by React. `Sidenav` is the component that we created above.

```tsx
import { Box, Grid, GridItem } from "@chakra-ui/react";

export function SidenavContainer({ children, sidenav }: SidenavContainerProps) {
  return (
    <Grid templateAreas={`'sidebar main'`} templateColumns="auto 1fr">
      <GridItem area="sidebar" as="aside" w="full" p={0}>
        <Box
          pos="sticky"
          top={0}
          w={{ base: 0, md: "72px" }}
          borderRight="1px solid"
          borderColor="gray.100"
          p={{ base: 0, md: 2 }}
          paddingTop={8}
          height="100vh"
          overflow="auto"
          css={{
            "&::-webkit-scrollbar": {
              height: "var(--chakra-sizes-1)",
              width: "var(--chakra-sizes-1)"
            },
            "&::-webkit-scrollbar-thumb": {
              backgroundColor: "var(--chakra-colors-gray-400)"
            }
          }}
        >
          {sidenav}
        </Box>
      </GridItem>
      <GridItem as="main" area="main" p={{ base: 6, md: 8 }}>
        {children}
      </GridItem>
    </Grid>
  );
}

export default SidenavContainer;
```

CSS `grid` is an ideal choice for creating two-dimensional layouts, and our case is no exception. Chakra UI conveniently provides us with a grid component out of the box, so we have opted to utilize it. If you would like to learn more about grids, you can refer to the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout)

**Tip:** *To conveniently organize and position elements on the grid, consider using the `templateAreas` property.*

In our grid, we have two items. The first item is our `sidenav`, and there are a few important details to note. We utilize the `as` prop to instruct Chakra UI to render the grid item as an `aside` element, which is crucial for accessibility reasons as mentioned earlier. Additionally, we make the sidenav `sticky`, ensuring it remains fixed when the user scrolls through the main content.

The second aspect to notice is `w={{ base: 0, md: "72px" }}`. By default, our sidenav has a `semi` mode, so we assign a width of `72px` to make it visible on the screen. For mobile devices, we transition from a two-dimensional layout to a one-dimensional layout, hiding the sidenav with `{base: 0}`. Similarly, we use the code `p={{ base: 0, md: 2 }}` to dynamically add or remove padding based on the device. 

The third important detail is the inclusion of `overflow="auto"`. This ensures that if we have an overflow of nav items, the sidenav won't break; instead, a scrollbar will be displayed, allowing users to access the additional items. We can apply custom styles to the scrollbar using the `css` prop, but for now, we won't delve into that since it can be modified according to your requirements.

The second item in our grid is the `main` content, which occupies the remaining space. Similar to the sidenav, we use the `as` prop to render it as a `main` element for accessibility reasons, as mentioned earlier. Furthermore, we adjust the padding based on different devices.

## Let’s try it out

Now we are done with the implementation. Let’s try it out in our React app. In your `App.tsx`, add the below code,

```tsx
import { BsBarChart } from "react-icons/bs";
import { BiMap, BiChalkboard } from "react-icons/bi";
import { FiSettings, FiMenu } from "react-icons/fi";
import { Outlet } from "react-router-dom";
import {
  SidenavProvider,
  SidenavContainer,
	SidenavItem,
  Sidenav,
	useSidenav,
} from "./components/sidenav";
import { Navbar } from "./components/navbar/navbar";

export default function App() {
  const navItems: SidenavItem[] = [
    { icon: BsBarChart, label: "Dashboard", to: "" },
    { icon: BiChalkboard, label: "Forecast", to: "forecast" },
    { icon: BiMap, label: "Location", to: "location" },
    { icon: FiSettings, label: "Settings", to: "settings" }
  ];
	const { onOpen } = useSidenav();
  return (
    <SidenavProvider>
      <SidenavContainer sidenav={<Sidenav navItems={navItems} />}>
        <main>
          <div className="App">
            <h1>Hello CodeSandbox!</h1>
            <h2>Start editing to see some magic happen!</h2>
            <Outlet />
          </div>
        </main>
	<IconButton
	   aria-label="menu"
	   display={{ base: "flex", md: "none" }}
	   onClick={onOpen}
	   icon={<FiMenu />}
	/>
      </SidenavContainer>
    </SidenavProvider>
  );
}
```
We have created an array of navigation items to define our navigation menu. To enable navigation between these items, you will need to set up the corresponding routes. Although I won't provide the code for creating routes here, I have prepared a codesandbox with the complete implementation, which is attached at the end.

Our entire app is wrapped in the `SidenavProvider`, allowing us to access the `SidenavContext` through the `useSidenav` hook. To demonstrate how to use the `useSidenav` hook, I have utilized an `<IconButton />` component, which is visible on mobile devices. When the user clicks on this button, it triggers the `onOpen` function from our hook, which opens the sidenav as a `drawer`.

![Full demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3e23lq95jkzjv8qvzmx.gif)


## Closing Remarks
Remember, the possibilities for customization and enhancement are endless. Feel free to explore and adapt the code according to your specific requirements and design preferences. With the tools and concepts discussed in this blog, you can create intuitive navigation solutions that elevate the usability and accessibility of your web applications.

Don't forget to check out the attached codesandbox for a full implementation example. If you have any questions or need further assistance, feel free to reach out. Happy coding and may your *navigation journeys* be smooth and delightful!.

{% embed https://codesandbox.io/embed/affectionate-brook-nrr1sx?fontsize=14&hidenavigation=1&theme=dark %}

### Inspirations:

[https://chakra-ui.com/](https://chakra-ui.com/)

[https://material.angular.io/components/sidenav/overview](https://material.angular.io/components/sidenav/overview)[https://web.dev/building-a-sidenav-component/](https://web.dev/building-a-sidenav-component/)

[https://web.dev/building-a-sidenav-component/](https://web.dev/building-a-sidenav-component/)

[https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/general-principles.html](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/general-principles.html)