React: Super Simple Side Menu Example

Here we will create a simple slide menu in react that will close when you click outside of it. This is a common pattern that you will see in many applications. We will be using React hooks to accomplish this.

useOutsideClick.ts

First lets create a util hook called useOutsideClick that will take a ref and a handler function. This hook will call the handler function when the user clicks outside of the ref.

import { useEffect, useCallback, useRef, type RefObject } from 'react'

function useOutsideClick<T extends HTMLElement>(
  ref: RefObject<T>,
  handler: (e: MouseEvent | TouchEvent) => void,
  when: boolean = true,
): void {
  const savedHandler = useRef(handler)

  const memoizedCallback = useCallback((e: MouseEvent | TouchEvent) => {
    if (ref && ref.current && !ref.current.contains(e.target as Node)) {
      savedHandler.current(e)
    }
  }, [])

  useEffect(() => {
    savedHandler.current = handler
  })
  useEffect(
    // eslint-disable-next-line
    () => {
      if (when) {
        document.addEventListener('click', memoizedCallback)
        document.addEventListener('touchstart', memoizedCallback)
        return () => {
          document.removeEventListener('click', memoizedCallback)
          document.removeEventListener('touchstart', memoizedCallback)
        }
      }
    },
    [ref, handler, when],
  )
}

export default useOutsideClick

Sidebar Component

Here we are using the useOutsideClick hook to close the menu when the user clicks outside of the menu. We are also using the cn package to conditionally add the side-menu--active class to the menu when the toggle button is clicked.

import { useState, useRef } from "react";
import cn from "classnames";
import useOutsideClick from "./useOutsideClick";
import "./styles.css";
const Sidebar = () => {
  const [toggle, setToggle] = useState(false);
  const pRef = useRef();
  const handleOutsideClick = () => setToggle(false);
  useOutsideClick(pRef, handleOutsideClick);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <div
        className={cn("side-menu", toggle && "side-menu--active")}
        ref={pRef}
      >
        <div>side menu</div>
        <button
          onClick={() => setToggle(!toggle)}
          className="side-menu__toggle"
        >
          click
        </button>
      </div>
    </div>
  );
};

CSS

.side-menu {
  position: absolute;
  left: 0;
  top: 20%;
  height: 200px;
  width: 100px;
  background: tomato;
  transform: translateX(-100%);
  transition: all 300ms ease-in-out;
}

.side-menu__toggle {
  position: absolute;
  left: 100px;
  top: 0;
  background: tomato;
  border: 0;
  color: blue;
  line-height: 2;
  border-radius: 0 5px 5px 0;
  cursor: pointer;
  border-left: 1px solid black;
}

For our CSS we are using the transform property to move the menu off the screen. We are also using the transition property to animate the menu when it is opened and closed. Since this is the initial state of the menu it is set to translateX(-100%). When the menu is opened we will set the transform property to translateX(0).

.side-menu--active {
  transform: translateX(0);
}

Demo

CodeSandbox