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;
}

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;

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;

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;

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;
}

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.

Posted on by:

hajarnasr profile

Hajar | هاجر

@hajarnasr

Trying to start a habit of writing about the things that I learn.

Discussion

pic
Editor guide