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 resize
eventListner 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>☰</span>:<span>×</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.
Top comments (0)