Clue Mediator

Sticky element on a scroll in React

📅March 5, 2022

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.

Output - Sticky element on a scroll in React - Clue Mediator

Output - Sticky element on a scroll in React - Clue Mediator

Steps to add a sticky element on a scroll

  1. Create a react application
  2. Add a listener for the sticky element
  3. Output

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.

App.js

import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';

function App() {
  const [sidebarWidth, setSidebarWidth] = useState(undefined);

  return (
    <div class="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 class="row">
        <div class="col-8">
          {[...Array(20)].map((a, i) => (
            <div key={i} class="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 class="col-4">
          <div class="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.

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.

index.css

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.

App.js

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 class="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 class="row">
        <div class="col-8">
          {[...Array(20)].map((a, i) => (
            <div key={i} class="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 class="col-4">
          <div class="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.

Demo & Source Code

GitHub Repository StackBlitz Project