DEV Community

Cover image for One Practical Way to Use useEffect 🎯
LEONARDO PALMEIRO
LEONARDO PALMEIRO

Posted on

One Practical Way to Use useEffect 🎯

Let’s talk about a small UX detail that can make a big difference.
Imagine your application has a modal. The user opens it… but the background page is still scrollable. Suddenly, the focus is gone, the UI feels messy, and the experience isn’t as pleasant as it should be.
This is a surprisingly common issue and a perfect example of where useEffect can help us clean things up and improve the user experience.
In this post, I’ll show a simple and effective way to prevent background scrolling when a modal is open, using React’s useEffect.

Here’s what it looks like when the background screen is still scrollable.

Now, let’s take a closer look at how useEffect solves this problem


useEffect(() => {
  // This effect runs when the modal is mounted (opened).
  // It disables background scrolling by hiding the page overflow.
  document.body.style.overflow = 'hidden';

  return () => {
    // This cleanup function runs when the modal is unmounted (closed).
    // It restores the original scrolling behavior.
    document.body.style.overflow = 'unset';
  };
}, []); // The empty dependency array ensures this runs only once

Enter fullscreen mode Exit fullscreen mode

if you prefer here is the live code: https://stackblitz.com/~/github.com/LeonardoPalmeiro/vitejs-vite-5ccuxsat

more information about useEffect hook:

Modal.jsx


import { useEffect } from 'react';

function Modal({ onClose }) {
  useEffect(() => {
    // This effect runs when the modal is mounted (opened).
    // It disables background scrolling by hiding the page overflow.
    document.body.style.overflow = 'hidden';

    return () => {
      // This cleanup function runs when the modal is unmounted (closed).
      // It restores the original scrolling behavior.
      document.body.style.overflow = 'unset';
    };
  }, []); // The empty dependency array ensures this runs only once

  return (
    <div className="modal-backdrop">
      <div className="modal-container">
        <button className="modal-close" onClick={onClose}>
          ×
        </button>

        <h2 className="modal-title">Modal Title</h2>
        <p className="modal-body">
          This is a centered modal with clean styling.
        </p>
        <div className="modal-actions">
          <button onClick={onClose}>Close</button>
        </div>
      </div>
    </div>
  );
}

export default Modal;
Enter fullscreen mode Exit fullscreen mode

App.jsx

import './App.css';
import Modal from './modal';
import { useState } from 'react';

function App() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
    {/* Add a tall container to enable scrolling */}
    <div style={{ height: '200vh', background: 'linear-gradient(white, gray)' }}>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      {isOpen && (
       <Modal onClose={() => setIsOpen(false)} />
      )}
    </div>
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

App.css

/* Backdrop */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  backdrop-filter: blur(2px);
}

/* Modal container */
.modal-container {
  position: relative;
  width: 100%;
  max-width: 480px;
  background: #ffffff;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
  animation: modalFadeIn 0.25s ease-out;
}

/* Animation */
@keyframes modalFadeIn {
  from {
    opacity: 0;
    transform: translateY(-20px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Close button */
.modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  border: none;
  background: transparent;
  font-size: 24px;
  cursor: pointer;
  color: #666;
}

.modal-close:hover {
  color: #000;
}

/* Content */
.modal-title {
  margin: 0 0 12px;
  font-size: 20px;
  font-weight: 600;
}

.modal-body {
  font-size: 15px;
  color: #555;
  margin-bottom: 20px;
}

/* Actions */
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}

/* Buttons */
.btn {
  padding: 8px 16px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
  font-weight: 500;
}

.btn.primary {
  background: #2563eb;
  color: white;
}

.btn.primary:hover {
  background: #1e4ed8;
}

.btn.secondary {
  background: #e5e7eb;
  color: #111;
}

.btn.secondary:hover {
  background: #d1d5db;
}

/* Mobile */
@media (max-width: 480px) {
  .modal-container {
    margin: 16px;
    padding: 20px;
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)