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.