DEV Community

loading...

Responsive Header in React

hajarnasr profile image Hajar | هاجر ・3 min read

On Netlify => Code

I used to think that making a responsive header in React would be complicated but once I started to implement it, it was much simpler than I'd thought.

I guess the first thing to think of when creating a responsive header is the way to measure the screen whenever it resizes. And fortunately for us, javascript provides an eventListener for exactly that purpose, the resize eventListener.

Let's see how we can take advantage of that eventListener to achieve our goal of making a responsive header.

// CustomHooks.js
import { useEffect, useState } from 'react';

export const useWindowWidthAndHeight = ()=>{
    // 1- Get the size of window 
    let windowInnerSize = [window.innerWidth, window.innerHeight];

    // 2- Define the state variable windowSize and pass windowInnerSize as its initial value
    let [ windowSize, setWidowSize ] = useState(windowInnerSize);

    useEffect(()=>{
        const changeWindowSize = ()=>{
            setWidowSize([window.innerWidth, window.innerHeight]);
        }
        /* 3- add a 'resize' eventListener to window so that whenever 
the size of window changes the state variable windowSize changes and the component re-renders */
        window.addEventListener("resize", changeWindowSize);

        // 4- cleanup the 'resize' eventListener
        return ()=> window.removeEventListener('resize', changeWindowSize);
    }, []);
    // 5- return the window size
    return windowSize;
}
Enter fullscreen mode Exit fullscreen mode

Here, we create a custom hook called useWindowWidthAndHeight.

Inside useWindowWidthAndHeight:
First, we create the windowInnerSize variable to store the current width and height of window.
Second, we use windowInnerSize as the initial value for the state variable windowSize that we created using the useState Hook.
Third, we declare a useEffect Hook to add a resizeeventListner so that when a window resize happens, changeWindowSize()changes windowSize to the new measured width and height of the screen.
Finally, we return the latest measured width and height of window.

Now, let's create a header.

// Header.js
import React from 'react';
import Navbar from './Navbar';
import { Link } from 'react-scroll'; // react-scroll is a library for scrolling in React
import SmallScreensNavbar from './SmallScreensNavbar';
import { useWindowWidthAndHeight } from './CustomHooks';

const Header = () =>{
    // use our custom hook to get the the window size
    const [width, height] = useWindowWidthAndHeight();
    return(
        <header>
            <div className="header-inner">
                <Link to="Home" 
                      smooth={true} 
                      className="logo nav-link">
                      RH
                </Link>
                {/*if the width of the window is bigger than 1000px use <Navebar/>,
                   else user <SmallScreensNavbar/>*/}
                { width > 1000 ?
                <Navbar navClass="nav-big"
                        linkClassName="nav-big-link"/>
                :
                <SmallScreensNavbar navClass="nav-small"
                                    linkClassName = "nav-small-link"
                />
                } 
            </div>
        </header>
    )
}

export default Header;
Enter fullscreen mode Exit fullscreen mode

Now, we need to create two navbar components, one for the screens with widths bigger than 1000px and another for smaller screens.

//Navbar.js
import React from 'react';
import { Link } from 'react-scroll';

const Navbar = ({navClass, linkClassName}) =>(
    <NavComponent navClass={navClass}
                  linkClassName = {linkClassName}
    />
);

export const NavComponent = ({onClick, navClass, linkClassName})=>(
  <nav className={navClass}>
      {["Projects", "About", "Contact", "Footer"].map(section=>
        <Link to={section}
              smooth={true}
              className={linkClassName}
              onClick={onClick}>
            {section}
        </Link>
      )}
  </nav>
)
export default Navbar;
Enter fullscreen mode Exit fullscreen mode

And for smaller screens:

//SmallScreensNavbar.js
import React, { useState } from 'react';
import { NavComponent } from './Navbar';

const SmallScreensNavbar = () =>{
    // declare 'translate' as a state variable
    let [translate, setTranslate ] = useState(true);
    return(
        <div> 
             <button className="hamburger-btn"
                     onClick= {()=> setTranslate(!translate)}>  {/* toggle translate */}
                  {/* change the btn text based on whether translate is true or false */} 
                 {translate?<span>&#9776;</span>:<span>&times;</span>}
             </button>
             {/*hide and show the sidebar list based on whether translate is true or false*/}
             <div id="sidebar-list" className={`${translate? "addTransiton": "removeTransition"}`}>
                <NavComponent
                    navClass="nav-small"
                    linkClassName = "nav-small-link"
                    onClick = {()=>setTranslate(true)}  //set translate to true to hide the sidebar list
                />
             </div>
        </div>
    )
}
export default SmallScreensNavbar;
Enter fullscreen mode Exit fullscreen mode

Now, for this to work we need to add the CSS:

header{
    position: fixed;
    top: 0;
    height: 70px;
    background-color: rgb(197, 178, 178);
    width: 100%;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    box-shadow: 1px 1px 1px 1px rgba(116, 110, 110, 0.199);
}
.header-inner{
    width: 90%;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.hamburger-btn{
    font-size: 1.3rem;
    position: absolute;
    bottom: 0;
    width: 40px;
    height: 35px;
    right: 30px;
    outline: none;
    background-color: rgb(134, 125, 125);
    color: whitesmoke;
}
.addTransiton{
    transform: translateX(170px);
    transition: transform 0.5s ease-in-out;
}
.removeTransition{
    transform: translateX(20px);
    transition: transform 0.5s ease-in-out;
}

#sidebar-list{
    background-color: rgb(134, 125, 125);
    height: 90vh;
    width: 170px;
    position: absolute;
    z-index: 2000;
    right: 0;
    top: 0;
    margin-top: 70px;
}
.nav-big{
    list-style: none;
    display: flex;
    justify-content: space-around;
    width: 70%;
    font-weight: bold;
}

.nav-big-link{
   cursor: pointer;
   color: white;
   text-decoration: none !important;
}

.nav-small{
    display: flex;
    flex-direction: column;
    text-align: center;
    justify-content: space-around;
    margin: auto;
    height: 40%;
    margin-top: 50px;
    width: 80%;
}
.nav-small-link{
    cursor: pointer;
    color: whitesmoke;
    padding-bottom: 5px;
}

Enter fullscreen mode Exit fullscreen mode

And things should be working now.
Thank you so much for reading. :)

References:
Stackoverflow to find out how to measure the screen's width and height.

Discussion (0)

pic
Editor guide