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

14

55

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.

Boltzmann answered 15/1, 2018 at 19:18 Comment(1)
Sheryl's answer is correct - but just so that you know, "el" usually refers to a ref and is short for "element".Kunming
B
35

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

The problem lies here: Depending on when it evaluates [email protected]:/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.

Boltzmann answered 15/1, 2018 at 20:38 Comment(3)
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.Mickiemickle
It would be useful to have a new solution now that componentWillMount() is deprecated. I couldn't find one.Pharyngoscope
useEffect(..., []) in a functional component does the job.Capet
S
62

Add ariaHideApp={false} to Modal attributes.

This should work:

<Modal isOpen={!!props.selectedOption}
    onRequestClose={props.clearSelectedOption}
    ariaHideApp={false}
    contentLabel="Selected Option"
    >
</Modal>
Showoff answered 31/5, 2018 at 13:5 Comment(2)
From the official docs: It is not recommended, but you can opt-out by setting ariaHideApp={false}.Maltzman
Better would be to do what it says, since that avoids the accessibility problem the warning refers to.Battiste
B
35

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

The problem lies here: Depending on when it evaluates [email protected]:/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.

Boltzmann answered 15/1, 2018 at 20:38 Comment(3)
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.Mickiemickle
It would be useful to have a new solution now that componentWillMount() is deprecated. I couldn't find one.Pharyngoscope
useEffect(..., []) in a functional component does the job.Capet
C
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.

Cynthia answered 2/9, 2018 at 0:41 Comment(2)
You might need a fallback for type safety as getElementById is nullable.Appetitive
@MoBeigi appElement={document.getElementById('root') || undefined} seems to sufficeNeubauer
G
13

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>
Gemmation answered 13/2, 2019 at 21:47 Comment(2)
It works also in react typescript project.So,upvoted.Variolous
For Next.js + Typescript use <Modal ... appElement={document.getElementById('__next') as HTMLElement} ... />Stubstad
B
8

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.
Brython answered 25/3, 2019 at 13:18 Comment(2)
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!Congeneric
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 <body> tag at the top of the tree. So I tried throwing that in there instead of the * and it worked!Congeneric
B
4

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

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

Bizarre answered 4/11, 2019 at 10:46 Comment(0)
H
4

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

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

Higherup answered 31/10, 2020 at 6:14 Comment(0)
I
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.

Instead answered 22/4, 2018 at 19:42 Comment(2)
react-modal has been fixed upstream, so this should not be a problem anymore.Instead
Its not, I updated the module but still get this warning.Simplicidentate
D
3

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')
Declivitous answered 24/6, 2022 at 15:21 Comment(0)
B
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/

Belicia answered 2/10, 2018 at 14:44 Comment(0)
H
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
Hamid answered 25/8, 2019 at 16:49 Comment(0)
N
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>
Nucleon answered 20/7, 2023 at 4:41 Comment(1)
Hey, why not just document.querySelector('body')?Lindsaylindsey
T
0

If having this issue with jest, a good solution is to add this to jest.polyfills.js (we have an old frankenstein app).

// React Modal Modal Setup
const Modal = require('react-modal')
Modal.setAppElement('body')
Terris answered 17/1 at 16:46 Comment(0)
S
-3

Delete this attrib className="modal" and run again

Superordinate answered 13/11, 2020 at 7:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.