49

How do I fix this warning in console of a React app using the react-modal package:

Warning: react-modal: App element is not defined. Please use Modal.setAppElement(el) or set appElement={el}

I have not been successful at figuring out what el is supposed to be.

Context: in my App.js root component file:

...
import Modal from 'react-modal';
...
class App extends Component {
  ...
  render(){
    ...  
    <Modal
      className="modal"
      overlayClassName="overlay"
      isOpen={foodModalOpen}
      onRequestClose={this.closeFoodModal}
      contentLabel="Modal"
    >
    ...
  }
}

Where ... indicates code not shown.

Everything works fine, but when the Modal is opened, the following Warning appears in my console:

index.js:2177 Warning: react-modal: App element is not defined. Please use Modal.setAppElement(el) or set appElement={el}. This is needed so screen readers don't see main content when modal is opened. It is not recommended, but you can opt-out by setting ariaHideApp={false}.

In the react-modal docs all I can find is the following:

App Element The app element allows you to specify the portion of your app that should be hidden (via aria-hidden) to prevent assistive technologies such as screenreaders from reading content outside of the content of your modal.

If you are doing server-side rendering, you should use this property.

It can be specified in the following ways:

DOMElement
Modal.setAppElement(appElement);
query selector - uses the first element found if you pass in a class.
Modal.setAppElement('#your-app-element');

Unfortunately, this has not helped! I cannot figure out what el is supposed to represent.

Here are some of the many property variations I have tried adding to my Modal component:

`appElement={el}`,  
`appElement="root"` where `root` is the id that my App component is injected into   
`appElement={'root'}`   
`appElement="div"`,   
`appElement={<div>}`,   
`appElement={"div"}`  

I've also tried calling Modal.setAppElement('root'); from inside src/index.js, where root is the root element that my App component is injected into, and index.js is where I do that.

TylerH
  • 20,799
  • 66
  • 75
  • 101
SherylHohman
  • 16,580
  • 17
  • 88
  • 94

13 Answers13

54

Add ariaHideApp={false} to Modal attributes.

This should work:

<Modal isOpen={!!props.selectedOption}
    onRequestClose={props.clearSelectedOption}
    ariaHideApp={false}
    contentLabel="Selected Option"
    >
</Modal>
SherylHohman
  • 16,580
  • 17
  • 88
  • 94
amrit chhetri
  • 785
  • 8
  • 12
32

Some solutions are given in react-modal issue #133:

The problem lies here: Depending on when it evaluates react-modal@1.6.5:/lib/helpers/ariaAppHider.js#L1:

  • document.body does not exist yet and it will resolve to undefined || null.
  • if Modal.setAppElement() is called with null or not called at all with the <script /> placed on <head /> (same as above).
  • Probably it can also happen if called with a selector that does not match any results.

Solutions:

Browser Rendering:

@yachaka snippet prevents this behavior by defining the element before placing the <Modal />:

componentWillMount() {
    Modal.setAppElement('body');
}

@ungoldman answer, if you don't want to depend on `setAppElement':

Inject the bundled application JS into <body> instead of <head>.
Though ideally react-modal should wait until the DOM is loaded to try attaching to document.body.

server-side:

If rendering on server-side, you must provide a document.body, before requiring the modal script (perhaps it should be preferable to use setAppElement() in this case).


Update: react docs have been updated to include the information above, so they should now be clearer for users running into this issue.
react-modal issue #567: add information (from issue #133 linked above) to the docs.

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
  • if the modal is used within a component only, can it be mounted such that the overlay covers only that component - and not parent / grandparents of that component ? I tried it, it didn't seem to work, it went back to covering the whole screen. – joedotnot Oct 31 '18 at 09:20
  • 2
    It would be useful to have a new solution now that componentWillMount() is deprecated. I couldn't find one. – Jamie McLaughlin Apr 05 '20 at 10:14
20

Just include appElement={document.getElementById('app')} inside your modal like this

<Modal
className="modal"
appElement={document.getElementById('app')}
>

It will work 100% if app is your central in index.html from where react loads.

Aditya Mishra
  • 311
  • 2
  • 3
12

This is my TypeScript Modal component which wraps react-modal v3.8.1:

import React from 'react'
import ReactModal from 'react-modal'

interface Props {
  isOpen: boolean
  ariaLabel?: string
}

const Modal: React.FC<Props> = ({
  children,
  ariaLabel = 'Alert Modal',
  isOpen,
}) => (
  <ReactModal
    appElement={document.getElementById('root') as HTMLElement}
    ariaHideApp={process.env.NODE_ENV !== 'test'}
    isOpen={isOpen}
    contentLabel={ariaLabel}
    testId="modal-content"
  >
    {children}
  </ReactModal>
)

export default Modal

Usage in component with state = { isOpen: true }:

<Modal isOpen={this.state.isOpen}>
  <p>
    Modal Content here…
  </p>
  <button onClick={() => { this.setState({ isOpen: false }) }}>Okay</button>
</Modal>
Beau Smith
  • 33,433
  • 13
  • 94
  • 101
7

If getting the Warning: react-modal: App element is not defined... error when running tests (we were running Jest), you can suppress the warnings by adding the following to your test file:

import ReactModal from 'react-modal';
ReactModal.setAppElement('*'); // suppresses modal-related test warnings.
JoeTidee
  • 24,754
  • 25
  • 104
  • 149
  • It feels dirty, I wish I could figure out the actual selector to pass in there to get it to work. But for now, this at least suppressed the warning in my tests, so I'm happy! – dmikester1 Dec 30 '22 at 06:10
  • Update on my previous comment. Once I got past that issue and I could see the dom tree printed out, I saw there was a `` tag at the top of the tree. So I tried throwing that in there instead of the * and it worked! – dmikester1 Dec 30 '22 at 06:32
4

The shortest solution is to add
appElement={document.getElementById("hereIsYourRootElementId")}

It lets react-modal know where is your root element.

ksav
  • 20,015
  • 6
  • 46
  • 66
4

Just put this
Modal.setAppElement('#root')

This will solve the warning. The root element coming from inside public folder index.html.

ksav
  • 20,015
  • 6
  • 46
  • 66
3

For reference, since it was a pain for me, if you are doing SSR, use the following code to prevent errors server-side:

if (typeof(window) !== 'undefined') {
    ReactModal.setAppElement('body')
}

You could put this in componentDidMount() anywhere you use a modal or I put it in a custom modal component so it's nice and DRY.

Nathaniel Hill
  • 423
  • 4
  • 7
2

you need to add # before your root element id.

import React from 'react';
import Modal from 'react-modal';


Modal.setAppElement('#root');

const OptionModal = (props) => (
  <Modal
    isOpen={!!props.selectedOption}
    contentLabel='this is the selected option'
  >
    <h3>Selected Option</h3>
    {props.selectedOption && <p>{props.selectedOption}</p>}
    <button onClick = {props.handleCloseOptionModal}>Close</button>
  </Modal>
);

export default OptionModal;

here is the reference: http://reactcommunity.org/react-modal/accessibility/

Mo Hemati
  • 251
  • 1
  • 11
2

For Nextjs, I think you can solve this by adding the below to outside your modal component, maybe on top, before the component is declared.

Modal.setAppElement('#__next')
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
1

If you get that warning on testing with the "react-testing-library" here is a solution: https://github.com/reactjs/react-modal/issues/576#issuecomment-524644035

using the react-testing-library (https://testing-library.com/) I get rid of that warning with:

import Modal from "react-modal";

const { container } = render(<MyComponent />);
Modal.setAppElement(container);

....  // to the testing, use Modal

or, if you want to test the modal component directly:

const { container, rerender } render(<MyModalComponent isOpen={false} />);
Modal.setAppElement(container);
// now the appElement is set we can show the modal component
rerender(<MyModalComponent isOpen={false} />);

....  // to the testing
Reinhard
  • 1,516
  • 1
  • 18
  • 25
0

SOLVED IN NEXTJS: This solved it for me, I simply added an id to the root body in the layout, followed by appElement={document.getElementById("root")} in the modal.

layout.js Component

<body suppressHydrationWarning={true} id='root'>
     <div className='main-container col-12 d-flex flex-row h-100 w-100 col-12 justify-content-start align-items-start'>
          <main className='col-10'>{children}</main>
     </div>
</body>

some Component

<Modal
        isOpen={modalIsOpen}
        onRequestClose={closeModal}
        contentLabel='Book Modal'
        appElement={document.getElementById("root")}
        style={{
          content: {
            width: "850px",
            height: "85vh",
            margin: "auto",
            padding: "0px",
            border: "none",
            overflow: "hidden",
          },
        }}
      >
      <h1>Content Of Modal Goes Here</h1>
</Modal>
-3

Delete this attrib className="modal" and run again