How to Mock Forwarding Refs in React

This article explains how to successfully mock components that use React’s forwardRef API during unit testing. You will learn how to mock these components using Jest and React Testing Library to prevent test failures and ensure your component interactions are verified correctly.

The Problem with Mocking forwardRef

When you mock a standard React component in Jest using jest.mock(), Jest replaces the component with a simple dummy function. However, if the component being mocked uses React.forwardRef, the parent component passing the ref expects a valid React ref object or callback. If Jest replaces it with a standard mock function, React will throw an error or the ref will resolve to undefined, breaking any DOM interactions or method calls (such as .focus()).

Solution 1: Mocking the Component with forwardRef

To mock a child component that uses forwardRef, you must explicitly return a component wrapped in React.forwardRef inside your jest.mock implementation.

Here is how to do it:

import React from 'react';
import { render, screen } from '@testing-library/react';
import ParentComponent from './ParentComponent';

// Mock the child component that uses forwardRef
jest.mock('./ChildComponent', () => {
  const React = require('react');
  // Return a mocked component wrapped in forwardRef
  return React.forwardRef((props, ref) => (
    <div ref={ref} data-testid="mocked-child">
      {props.children}
    </div>
  ));
});

describe('ParentComponent', () => {
  it('renders the mocked child with the ref attached', () => {
    render(<ParentComponent />);
    const childElement = screen.getByTestId('mocked-child');
    expect(childElement).toBeInTheDocument();
  });
});

By requiring react inside the factory function and returning React.forwardRef, you ensure that the React reconciler correctly attaches the ref to the mocked DOM element.

Solution 2: Mocking useImperativeHandle Custom Methods

Often, components that use forwardRef also use useImperativeHandle to expose custom imperative methods (like a custom .focus() or .clear() method) to the parent.

If the parent component calls one of these custom methods on the ref, your mock must implement useImperativeHandle to prevent “is not a function” errors:

jest.mock('./CustomInputComponent', () => {
  const React = require('react');
  return React.forwardRef((props, ref) => {
    // Implement useImperativeHandle to mock the exposed custom methods
    React.useImperativeHandle(ref, () => ({
      focus: jest.fn(),
      customClear: jest.fn(),
    }));

    return <input data-testid="mocked-input" />;
  });
});

This setup ensures that when the parent component executes ref.current.focus(), Jest executes the mock function instead of throwing a runtime error.

Solution 3: Passing a Mocked Ref in Tests

If you are testing the component that uses forwardRef directly and want to verify that it correctly assigns the ref to its inner DOM element, you can pass a created ref directly in your test:

import React from 'react';
import { render } from '@testing-library/react';
import MyForwardRefComponent from './MyForwardRefComponent';

it('correctly forwards the ref to the input element', () => {
  const ref = React.createRef();
  render(<MyForwardRefComponent ref={ref} />);
  
  // Verify the ref points to the actual DOM node
  expect(ref.current).toBeInstanceOf(HTMLInputElement);
});