Add a class to the HTML <body> tag with React?

HtmlReactjsClassname

Html Problem Overview


I'm making a modal in my React project that requires a class to be added to the body when the modal is open and removed when it is closed.

I could do this the old jQuery way by running some vanilla JavaScript which adds / removes a class, however this doesn't feel like the normal React philosophy.

Should I instead setState on my top level component to say whether the modal is open or closed? Even if I did this, as it's rendered into the div on the page it's still a side-effect to edit the body element, so is there any benefit for this extra wiring?

Html Solutions


Solution 1 - Html

TL;DR use document.body.classList.add and document.body.classList.remove

I would have two functions that toggle a piece of state to show/hide the modal within your outer component.

Inside these functions I would use the document.body.classList.add and document.body.classList.remove methods to manipulate the body class dependant on the modal's state like below:

openModal = (event) => {
  document.body.classList.add('modal-open');
  this.setState({ showModal: true });
}
hideModal = (event) => {
  document.body.classList.remove('modal-open');
  this.setState({ showModal: false });
}

Solution 2 - Html

With the new React (16.8) this can be solved with hooks:

import {useEffect} from 'react';

const addBodyClass = className => document.body.classList.add(className);
const removeBodyClass = className => document.body.classList.remove(className);

export default function useBodyClass(className) {
    useEffect(
        () => {
            // Set up
            className instanceof Array ? className.map(addBodyClass) : addBodyClass(className);

            // Clean up
            return () => {
                className instanceof Array
                    ? className.map(removeBodyClass)
                    : removeBodyClass(className);
            };
        },
        [className]
    );
}

then, in the component

export const Sidebar = ({position = 'left', children}) => {
    useBodyClass(`page--sidebar-${position}`);
    return (
        <aside className="...">
            {children}
        </aside>
    );
};

Solution 3 - Html

Actually you don't need 2 functions for opening and closing, you could use document.body.classList.toggle

const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
  document.body.classList.toggle('modal-open', isOpen);
},[isOpen])
    
<button onCLick={()=> setIsOpen(!isOpen)}>Toggle Modal</button>

Solution 4 - Html

Like what @brian mentioned, try having a top-level container component that wraps around your other components. (assuming you're not using redux in your app)

In this top-level component:

  1. Add a boolean state (eg. modalOpen) to toggle the CSS class
  2. Add methods (eg. handleOpenModal & handleCloseModal) to modify the boolean state.
  3. Pass the methods created above as props into your <Modal /> component

Solution 5 - Html

ReactJS has an official React Modal component, I would just use that: https://github.com/reactjs/react-modal

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEvanssView Question on Stackoverflow
Solution 1 - HtmlSam LoganView Answer on Stackoverflow
Solution 2 - HtmlLukas KralView Answer on Stackoverflow
Solution 3 - HtmlBojan MiticView Answer on Stackoverflow
Solution 4 - HtmlJustinTohView Answer on Stackoverflow
Solution 5 - HtmlswyxView Answer on Stackoverflow