DEV Community

Cover image for Simple hamburger button in react-typescript and tailwind CSS
Ondřej Tuček
Ondřej Tuček

Posted on • Edited on

Simple hamburger button in react-typescript and tailwind CSS

Hello all 👋

Today I will be creating a hamburger menu within react, typescript and tailwind css.

First things first, before we start we will be needing a few things.

  1. Working machine with an IDE or text editor
  2. Working react environment with typescript and tailwind support. (I recommend vite)
  3. Coffee or drink of your choice. (I am going with Kofola)

Preparing props and methods

So before we actually start typing our component, I would like to create an interface for it.
For those who don't know what interface is, basically it just is a block of code, that we use to tell us how are objects shaped.

// file: ~/components/hamburger/Hamburger.tsx

export interface HamburgerProps {
    /** Callback function, which should be executed on click */
    onClick: () => void;

    /** Initial state of our button */
    isInitiallyOpen?: boolean;
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we don't need much there. If you are fancy enough through, you can extend it with for example ButtonHTMLAttributes<HTMLButtonElement> with this your component will inherit every property and methods of react button component.

Component structure

// file: ~/components/hamburger/Hamburger.tsx
...

export function Hamburger(props: HamburgerProps) {
    const { onClick, isInitiallyOpen } = props;
    const [isOpen, setIsOpen] = useState<boolean>(isInitiallyOpen ?? false);

    const handleClick = () => {
        setIsOpen(prev => !prev);
        onClick();
    }

    return (
        <button onClick={handleClick} type="button" className={``}>
            <div className={``}/>
            <div className={``}/>
            <div className={``}/>
        </button>
    );
}
Enter fullscreen mode Exit fullscreen mode

So there is the basic structure of our component. As you can see it's pretty straight forward, we just have a button with three divs and a simple onClick handler.

Styling the component

// file: ~/components/hamburger/Hamburger.tsx
...

export function Hamburger(props: HamburgerProps) {
    ...

    return (
        <button onClick={handleClick} type="button" className={`w-8 h-8 flex justify-around flex-col flex-wrap z-10 cursor-pointer`}>
            <div className={`bg-black block w-8 h-[0.35rem] rounded transition-all origin-[1px]`}/>
            <div className={`bg-black block w-8 h-[0.35rem] rounded transition-all origin-[1px]`}/>
            <div className={`bg-black block w-8 h-[0.35rem] rounded transition-all origin-[1px]`}/>
        </button>
    );
}
Enter fullscreen mode Exit fullscreen mode

Next, we add the effect itself.

// file: ~/components/hamburger/Hamburger.tsx
...

export function Hamburger(props: HamburgerProps) {
    ...

    return (
        <button onClick={handleClick} type="button" className={`w-8 h-8 flex justify-around flex-col flex-wrap z-10 cursor-pointer`}>
            <div className={`... ${isOpen ? 'rotate-45' : 'rotate-0'}`}/>
            <div className={`... ${isOpen ? 'translate-x-full bg-transparent' : 'translate-x-0'}`}/>
            <div className={`... ${isOpen ? 'rotate-[-45deg]' : 'rotate-0'}`}/>
        </button>
    );
}
Enter fullscreen mode Exit fullscreen mode

Hooray! With this done, we should be able to actually see the component in its final form.

Hamburger effect in action

Final code

// file: ~/components/hamburger/Hamburger.tsx

export interface HamburgerProps {
    /** Callback function, which should be executed on click */
    onClick: () => void;

    /** Initial state of our button */
    isInitiallyOpen?: boolean;
}

export function Hamburger(props: HamburgerProps) {
    const { onClick, isInitiallyOpen } = props;
    const [isOpen, setIsOpen] = useState<boolean>(isInitiallyOpen ?? false);

    const handleClick = () => {
        setIsOpen((prev) => !prev);
        onClick();
    };

    return (
        <button
            onClick={handleClick}
            type="button"
            className={`w-8 h-8 flex justify-around flex-col flex-wrap z-10 cursor-pointer`}
        >
            <div
                className={`bg-black block w-8 h-[0.35rem] rounded transition-all origin-[1px] ${
                    isOpen ? 'rotate-45' : 'rotate-0'
                }`}
            />
            <div
                className={`bg-black block w-8 h-[0.35rem] rounded transition-all origin-[1px] ${
                    isOpen ? 'translate-x-full bg-transparent' : 'translate-x-0'
                }`}
            />
            <div
                className={`bg-black block w-8 h-[0.35rem] rounded transition-all origin-[1px] ${
                    isOpen ? 'rotate-[-45deg]' : 'rotate-0'
                }`}
            />
        </button>
    );
}
Enter fullscreen mode Exit fullscreen mode

Using this component is very simple, all it requires is a callback function.

I like to use this setup with sidebar menu. So I have useState with current state of sidebar and I have function to change the state. I use both of these as props for <Hamburger/> component.

I hope I helped or entertained you enough, and please excuse the quality of this post, it is my first post here at dev.to.

Top comments (0)