In many modern websites, you must have seen this effect of Navigation bar/ Header sticking to the top of the page even if you scroll down the page for your better navigation so you don’t have to go up always to choose between links for moving between the pages. Haven’t you?
If you have and are curious about how does that work, then this post is your go-to guide to create that very simple yet beautiful effect/feature for you next project which adds better UX to the page.
What are you waiting for? Let’s get started 🚀
Initial Setup
First, we are going to create a react-app from start. For that enter this command in your favourite terminal
create-react-app sticky-navbar
We are going to delete some of the files created out of the box from create-react-app. After deleting some files, project directory will look like this:
We will start by creating components, two components will suffice for this little project. Create a directory called Components in /src and add two files Navbar.js and Content.js
Start by editing App.js. Add the following code to start with a basic layout
//App.js
import React from 'react';
import Navbar from './components/Navbar';
import Content from './components/Content';
function App() {
return (
<div className="App">
<Navbar />
<Content />
</div>
);
}
export default App;
Here we are rendering two functional components Navbar and Content, their name clearly explains their purpose.
This below code fills up Navbar.js. Navbar component is basically a header section which consists of your website Logo and one navigation menu with 4/5 links
//Navbar.js
import React from 'react';
import './navbar.scss';
import Logo from './../img/logo.svg';
const Navbar=() => {
return (
<header className={navbarClasses.join(" ")}>
<div className="logo">
{/* your logo */}
</div>
<nav className="navigation">
{/* your navigation */}
</nav>
</header>
)
};
export default Navbar;
Now, it’s time to give some styling to the page. You can have your own set of styling or if you just want to use mine, copy below given styles to navbar.scss
//navbar.scss
.navbar{
width: 100%;
min-height: 6vh;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #eee;
transition: all .7s ease-in;
}
//IMPORTANT
.scrolled{
position: fixed;
top: 0;
left: 0;
background-color: lightblue;
}
.logo img{
width: 50px;
}
.navigation{
//your navigation styles
}
Now, we would fill up Content.js with some meat to add some content on the page along with the Navbar.
This code fills up dummy boxes to take ample amount of height on-page. You can use any real-life content. This is just for demo
//Content.js
import React from 'react';
import './content.scss';
const Content=() => {
const data=(
<div className="box">
<h2>My div content </h2>
</div>
);
return (
<main className="content">
{data}
{data}
{data}
{data}
{data}
</main>
)
}
export default Content;
Style for Content.js
//content.scss
.content{
width: 80%;
margin:2rem 10% 0 10%;
max-width: 100%;
box-sizing: border-box;
min-height: 100vh;
}
.box{
width:80%;
margin:2rem 10% 0 10%;
height:100vh;
background-color:plum;
}
After all this setup, our page will look like this:
Main Logic
Now that we have our meat set up for the Sticky navbar, we will add the main Logic to get that sticky effect. For this, we are going to do a little bit of state-management using React hooks. If you are not aware of react-hooks, don’t worry about it. You can use normal state management with classful component too.
So, the idea is as you scroll down, you check for the scrollY (i.e. total height you have scrolled from the top in pixels ) in componentDidMount() or useEffect() using window.scrollY and if that scrolled height is greater than some value, say 200px in this case, then we change the class of our Navbar to navbar scrolled. Styles for .scrolled is already in your navbar.scss if you have copied my styles. I have used array approach to switch between classes from navbar to navbar scrolled.
Confused?
//Navbar.js
import React,{useEffect} from 'react';
import './navbar.scss';
const Navbar=() => {
const [scrolled,setScrolled]=React.useState(false);
const handleScroll=() => {
const offset=window.scrollY;
if(offset > 200 ){
setScrolled(true);
}
else{
setScrolled(false);
}
}
useEffect(() => {
window.addEventListener('scroll',handleScroll)
})
let navbarClasses=['navbar'];
if(scrolled){
navbarClasses.push('scrolled');
}
return (
/* rest remains same*/
)
};
export default Navbar;
In this way, we have successfully created our simple sticky Navbar from scratch.
If you want to look at the complete code, check my GitHub repo.
Conclusion
This was all for this simple demo application. I hope this post was helpful to you and I’ve solved your problem by explaining it in your words. If you have any queries regarding this post, feel free to ask me personally. I will be happy to answer those on an individual level.
Thank you. Have a nice day!
Top comments (20)
Hi @rohit ,
Is there any codepen sample? Or somewhere you have hosted this code? Or GitHub repo?
Yes, @madhavbahlmd , I've given the GitHub link for the same at the end of the post.
Here is the link again: GitHub Repo
Sorry for the late reply!
suppose along with navbar there is header to means navbar is the subset of header in header there is company logo and in navbar other components are there
like home,service etc.
now i want to keep only my navbar component static and header component not.
Then what to do .
please help me with some article or any code suggestion.
Hey sorry for the late reply,
Can you elaborate more clearly on the problem, please?
I'm quite not clear on the asked question.
Thank you,
Hello buddy i attached an image that explain better.
Animated sticky header on scroll in React---this should be problem statement.
I don't see any image attached
here see
did you get ??
you should remove the event listener in the clean up function of useEffect to avoid memory leaks.
And provide an empty dependency array to useEffect to only register the event once when the component mounts.
thank you @rohit! worked amazing, need to adjust some overflow, but otherwise works like a charm!
Hi Rohit,
Thanks so much for this - I found it really helpful!
One slight change I made to my own code it I removed the navbarClasses (and the if block to push 'scrolled' onto the className and instead, in the function return, included the following: className={setScrolled? 'navbar scrolled' : 'navbar'} which seems like less overhead and transitions smoother (especially noticeable when you set the transition and conditional if block to check offset are both set to 0).
Thank you, Jeremy! Glad to hear you found it useful ;)
Yes, your approach to choosing appropriate className upon scroll is too perfect. No problem with it.
I just chose it for simplicity. Nothing else.
Happy coding :)
hey jeremy, can you provide the link to this code?
That was really a nice example to explain, can you please make another post on how we can align other component beneath the nav bar. Actually right now i am trying to code one react project in which i have to put my resume pdf beneath my navbar, but that is not happening.
Why do we need to use join() method? please explain:)
Hey Karem,
According to my approach,
navbarClasses
is an array of 2 strings. So, to make a single string from those, join() will make it happen.Another approach is to use Ternary operator like this:
I hope I've cleared it out ;)
Thanks Rohit! Great explanation :)
Thanks Rohit. This helped a lot, I was stuck on a project (migrating HTML to NEXT.js) trying to run jquery to implement sticky header.
Glad you found it helpful :)
Good day ;)
Hello Rohit, this is awesome. but i'll go with ternary.