How to Mock React Portals in React
Testing components that use React Portals can be challenging because they render children outside the main DOM hierarchy. This article provides a straightforward guide on how to mock React Portals using Jest and React Testing Library, allowing you to test your modal, tooltip, or overlay components inline within your test suite without managing external DOM nodes.
Why Mock React Portals?
React Portals are typically used to render elements into a DOM node
that exists outside the parent component’s DOM tree (like a
<div id="modal-root"></div> in your
index.html).
During unit tests, this target DOM node often does not exist in the
virtual DOM environment (jsdom). This causes
ReactDOM.createPortal to throw errors. Mocking the portal
mechanism allows you to bypass this issue by forcing the portal to
render its children directly into the component’s standard render tree
during testing.
Method 1: Mocking createPortal with Jest
The easiest way to mock React Portals is by using Jest to spy on
ReactDOM.createPortal and overriding its behavior to return
the children directly.
Add the following mock configuration at the top of your test file, or
in your setupTests.js file:
import ReactDOM from 'react-dom';
beforeAll(() => {
jest.spyOn(ReactDOM, 'createPortal').mockImplementation((element) => {
return element;
});
});
afterAll(() => {
ReactDOM.createPortal.mockRestore();
});By returning the element directly, you bypass the portal
functionality. React Testing Library can now query and assert against
the portal’s children as if they were standard child components in the
main render output.
Method 2: Creating the Target DOM Element (No Mocking)
If you prefer not to mock React internals, you can dynamically create the target container element in your test environment before each test runs. This is the recommended approach if you want to test the actual portal mounting behavior.
describe('Modal Portal Test', () => {
let portalRoot;
beforeEach(() => {
// Create the portal container and append it to the body
portalRoot = document.createElement('div');
portalRoot.setAttribute('id', 'modal-root');
document.body.appendChild(portalRoot);
});
afterEach(() => {
// Clean up the DOM after each test
document.body.removeChild(portalRoot);
});
it('renders inside the portal', () => {
render(<Modal />);
expect(screen.getByText('Modal Content')).toBeInTheDocument();
});
});Using this setup, the portal finds the #modal-root
element in the document body just as it would in a real browser
environment, allowing your assertions to pass without any mocks.