Sticky element on a scroll in React
If you want to add a sticky div or paragraph on page scroll then today we will show you how to add a sticky element on a scroll in React. Here, we will not use any NPM Package to achieve this functionality.
Checkout more articles on ReactJS
Output
For demostration, we will add the sticky sidebar on page scroll.
Steps to add a sticky element on a scroll
1. Create a react application
Let’s create a React application using the create-react-app
and render a component that contains the sidebar along with the content.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import React, { useState } from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; function App() { const [sidebarWidth, setSidebarWidth] = useState(undefined); return ( <div classname="app"> <h3>Sticky element on a scroll in React - <a href="https://www.cluemediator.com" target="_blank" rel="noreferrer noopener">Clue Mediator</a></h3> <div classname="row"> <div classname="col-8"> {[...Array(20)].map((a, i) => ( <div key="{i}" classname="item"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s. </div> ))} </div> <div classname="col-4"> <div classname="sidebar" style={{ width: sidebarwidth }}> <h3>Sidebar</h3> <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s. </p> </div> </div> </div> </div> ); } export default App; |
2. Add a listener for the sticky element
In the next step, we will add a listener for the sticky element. On page scroll, we will add/remove is-sticky
class to the sidebar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | const [sidebarWidth, setSidebarWidth] = useState(undefined); const [sidebarTop, setSidebarTop] = useState(undefined); useEffect(() => { const sidebarEl = document.querySelector('.sidebar').getBoundingClientRect(); setSidebarWidth(sidebarEl.width); setSidebarTop(sidebarEl.top); }, []); useEffect(() => { if (!sidebarTop) return; window.addEventListener('scroll', isSticky); return () => { window.removeEventListener('scroll', isSticky); }; }, [sidebarTop]); const isSticky = (e) => { const sidebarEl = document.querySelector('.sidebar'); const scrollTop = window.scrollY; if (scrollTop >= sidebarTop - 10) { sidebarEl.classList.add('is-sticky'); } else { sidebarEl.classList.remove('is-sticky'); } } |
Add the following CSS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } h1, p, div { font-family: Lato; padding: 10px; } .item { border: 1px solid #ddd; border-radius: 5px; padding: 10px; margin-bottom: 10px; } .sidebar { border: 1px solid #ddd; border-radius: 5px; padding: 10px; } .is-sticky { position: fixed; top: 10px; z-index: 999; animation: 500ms ease-in-out 0s normal none 1 running fadeInDown; } |
3. Output
Let’s put all the code together and see how it looks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import React, { useEffect, useState } from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; function App() { const [sidebarWidth, setSidebarWidth] = useState(undefined); const [sidebarTop, setSidebarTop] = useState(undefined); useEffect(() => { const sidebarEl = document.querySelector('.sidebar').getBoundingClientRect(); setSidebarWidth(sidebarEl.width); setSidebarTop(sidebarEl.top); }, []); useEffect(() => { if (!sidebarTop) return; window.addEventListener('scroll', isSticky); return () => { window.removeEventListener('scroll', isSticky); }; }, [sidebarTop]); const isSticky = (e) => { const sidebarEl = document.querySelector('.sidebar'); const scrollTop = window.scrollY; if (scrollTop >= sidebarTop - 10) { sidebarEl.classList.add('is-sticky'); } else { sidebarEl.classList.remove('is-sticky'); } } return ( <div classname="app"> <h3>Sticky element on a scroll in React - <a href="https://www.cluemediator.com" target="_blank" rel="noreferrer noopener">Clue Mediator</a></h3> <div classname="row"> <div classname="col-8"> {[...Array(20)].map((a, i) => ( <div key="{i}" classname="item"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s. </div> ))} </div> <div classname="col-4"> <div classname="sidebar" style={{ width: sidebarwidth }}> <h3>Sidebar</h3> <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s. </p> </div> </div> </div> </div> ); } export default App; |
Run the application and check the output in the browser.
It is giving me error
Uncaught (in promise) TypeError: Cannot read properties of null (reading ‘getBoundingClientRect’)
Hi Hafiz,
Can you please null check before accessing the
getBoundingClientReact
?