React forwardRef explained: Usage, alternatives, and React 19 update

When working with reusable components in React, passing a ref to access a child’s DOM node or instance can be tricky. That’s where forwardRef comes in.

How To Use ForwardRef In React

In this tutorial, we’ll explain how to use the forwardRef in React, what it does, and why it matters. forwardRef is a utility that lets you forward a ref in React through a component to one of its children — useful when building input wrappers, modals, or other components that need DOM access.

You’ll learn how to create refs, attach them to DOM elements, and wrap components with forwardRef. We’ll reference the official React docs throughout and provide hands-on examples to solidify each concept.

Note — As of React 19, forwardRef is deprecated for function components. This article covers usage in React 18 and earlier.

Editor’s Note This post was updated in June 2025 to clarify that the article applies to React 18 and earlier, noting the deprecation of forwardRef for function components starting with React 19. It also added a comparison of forwardRef, useRef, and other alternatives to guide when to use each.

Previously, this post was last updated by Jude Miracle in August 2024 to introduce the use of the useImperativeHandle React Hook for customizing instance values exposed when using refs. It also now covers integrating forwardRef with functional components and class components.

TL;DR: What is forwardRef in React?

forwardRef is a React utility that lets a parent component pass a ref through a child component to access the child’s DOM node or instance directly. This is super helpful when the child is wrapped by other components and doesn’t expose the ref by default.

It takes a functional component and returns a new one that forwards the ref to a specific child element.

 import React, { forwardRef, useRef } from 'react';

const FancyInput = forwardRef((props, ref) => (
<input ref={ref} {...props} />
));

function Parent() {
const inputRef = useRef(null);

const focusInput = () => {
inputRef.current.focus();
};

return (
<>
<FancyInput ref={inputRef} placeholder="Focus me with the button" />
<button onClick={focusInput}>Focus Input</button>
</>
);
}

Why is forwardRef important?

forwardRef allows for a more flexible and efficient component composition. When working with complex applications, there are cases where you need direct access to a child component’s DOM element or instance from a parent component. However, React’s default behavior doesn’t always allow this, especially when dealing with higher-order components (HOCs) or wrapper components.

By using forwardRef, you can pass a reference from a parent component to a child component, even if that child component is wrapped inside another component. This enables the parent component to directly interact with the child’s DOM element or instance.

How do refs work in React?

To understand ref forwarding, we must first understand what refs are. Refs are a way to access and interact with a DOM element directly. Refs allow you to bypass the typical React data flow and perform actions not achievable with props and state alone.

Refs are often used for tasks like setting focus on an input field, measuring the dimensions of an element, or triggering animations. For instance, you can use refs to give focus on an input field when a button is clicked:

import * as React from "react";
import ReactDOM from "react-dom";

export default function App() {
 const ref = React.useRef();

 function focus() {
   ref.current.focus();
 }

 return (
   <div className="App">
     <input ref={ref} placeholder="my input" />
     <button onClick={focus}>Focus</button>
   </div>
 );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Similarly, we could use JavaScript to achieve a similar effect. However, it is not recommended to do this, and it is even marked as a bad practice to access the DOM directly when using React. The vanilla JavaScript equivalent to focusing an element could look like the following code snippet:

document.getElementById('myInput').focus()

When to use refs in React

Many refs can be pointed to using forwardRef. In React, it’s generally recommended to use props and state to manage your component data flow. However, there are some situations where using refs can be helpful or even necessary. Here are some common use cases for refs in React:

  • Managing focus, text selection, or media playback: Refs can be used to manage focus on form elements, select text in an input or text area, or control media playback for audio or video elements
  • Triggering animations: If you need to trigger animations using external libraries like GSAP or Anime.js, you might need a direct reference to a DOM element, which can be achieved using refs
  • Integrating with third-party DOM libraries: When using third-party libraries that require direct access to DOM elements, refs can be useful for providing the necessary access
  • Measuring the dimensions or position of elements: Refs can help you measure the size, position, or scroll position of an element. This can be useful for tasks like implementing a custom scrollbar, creating tooltips, or building responsive components

While refs are powerful tools, they should be used sparingly and only when necessary. Excessive use of refs can lead to code that is harder to understand and maintain. Always opt to use props and state for data flow in React components when possible.

Working with refs in class components

In this section, we will focus specifically on working with refs in class components. Although React has moved towards functional components with React Hooks, it is still important to understand how to manage refs in class components, as they remain prevalent in many existing projects.

We will cover the process of creating, attaching, and using refs in class components, along with examples that illustrate common use cases. This knowledge will enable you to use refs effectively in class components and facilitate a smoother transition to functional components and Hooks when needed.

Creating refs in React

To create a ref, React provides a function called React.createRef. Once created, they can be attached to React elements via the ref attribute. When a component is constructed, refs get assigned to instance properties of that component, ensuring that they can be referenced anywhere in the component. Here’s what that looks like:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.newRef = React.createRef(); //newRef is now available for use throughout our component
  }
 ...
}

At this point, we have created a Ref called newRef. To use this Ref in our component, we simply pass it as a value to the ref attribute like this:

class MyComponent extends React.Component {
 ...
  render() {
    return <div ref={this.myRef} />;
  }
}

Here, we’ve attached the Ref and passed in the newRef as its value. As a result, we now can update this without changing the component’s state.

Attaching refs

In this section, we will discuss attaching refs in React, which is the process of relating a ref to a DOM element for direct DOM manipulation. This step is crucial in order to effectively work with refs and employ their potential in various use cases, such as managing focus, measuring element dimensions, or triggering animations.

We already covered how to create refs with createRef, so now we will relate it to a DOM element by using the ref prop:

<div ref={this.myRef} />

And, finally, when we are ready to access the DOM element later on in the component lifecycle, we can do something like this:

const divWidth = this.myRef.current.offsetWidth;

Let’s see this behavior with a complete example where we are going to attach a reference to an HTML video element and use React buttons to play and pause the video using the native HTML5 APIs of the video element:

import ReactDOM from "react-dom";
import React, { Component } from "react";

export default class App extends Component {
  constructor(props) {
    super(props);
    this.myVideo = React.createRef();
  }
  render() {
    return (
      <div>
        <video ref={this.myVideo} width="320" height="176" controls>
          <source
            src="https://res.cloudinary.com/daintu6ky/video/upload/v1573070866/Screen_Recording_2019-11-06_at_4.14.52_PM.mp4"
            type="video/mp4"
          />
        </video>
        <div>
          <button
            onClick={() => {
              this.myVideo.current.play();
            }}
          >
            Play
          </button>
          <button
            onClick={() => {
              this.myVideo.current.pause();
            }}
          >
            Pause
          </button>
        </div>
      </div>
    );
  }
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Here, we used ref to pause and play our video player by calling the pause and play methods on the video. When the pause or play button is clicked, the function will be called on the video player without a re-render.

Using refs with functional components

Refs cannot be attached to functional components, although we can define refs and attach them to either DOM elements or class components. The bottom line is that functional components do not have instances, so you can’t reference them.

However, if you must attach a ref to a functional component, the official React team recommends converting the component to a class, just like you would do when you need lifecycle methods or state.

Conditional refs

Aside from passing the default ref attribute, we can also pass functions to set refs. The major advantage of this approach is that you have more control over when refs are set and unset. That is possible because it allows us to determine the state of the ref before certain actions are fired.

Consider this snippet from the documentation page below:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = null;
    this.setTextInputRef = element => {
      this.textInput = element;
    };
    this.focusTextInput = () => {
      // Focus the text input using the raw DOM API
      if (this.textInput) this.textInput.focus();
    };
  }
  componentDidMount() {
    this.focusTextInput();
  }
  render() {
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

Instead of defining the refs in the constructor, we set the initial value to null. The benefit of this approach is that textInput will not reference a node until the component is loaded (when the element is created).

Forwarding refs in React using forwardRef

When a child component needs to reference its parent component’s current node, the parent component needs a way to send down its ref to the child. The technique is called ref forwarding, and it is very useful when building reusable component libraries. Ref forwarding can be achieved using the forwardRef function.

Let’s take an example of a new library with an InputText component that will provide a lot of functionality, though, for now, we’ll keep it simple:

const InputText = (props) => (
  <input {...props} />
));

The InputText() component will tend to be used throughout the application similarly to a regular DOM input, therefore accessing its DOM node may be unavoidable for managing focus, selection, or animations related to it.

In the example below, other components in the application have no access to the DOM input element generated by the InputText() component and are thus restricting some of the operations we have already foreseen we would need to meet our application requirements, such as controlling the focus of the input programmatically.

Here is when React.forwardRef enters to obtain a ref passed as props, and then forwards it to the DOM input that it renders, as shown below:

const InputText = React.forwardRef((props, ref) => (
 <input ref={ref} {...props} />
));

Now that our component supports forwardRef, let’s use it in the context of our application to build a button that will automatically focus the input when it’s clicked. The code looks as follows:

import * as React from "react";
import ReactDOM from "react-dom";

const InputText = React.forwardRef((props, ref) => (
 <input ref={ref} {...props} />
));

export default function App() {
 const ref = React.useRef();

 function focus() {
   ref.current.focus();
 }

 return (
   <div className="App">
     <InputText ref={ref} placeholder="my input" />
     <button onClick={focus}>Focus</button>
   </div>
 );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

In the code above, we defined a ref in the component that needs the ref and passed it to the button component. React passed the ref through and forwarded it down to <input ref={ref}> by specifying it as a JSX attribute. When the ref was attached, ref.current pointed to the <input> DOM node.

The second ref argument in the InputRef component only existed when you defined a component with a React.forwardRef call. Regular functional or class components didn’t receive the ref argument, and ref was not available in props. Ref forwarding is not limited to DOM components; you can also forward refs to class component instances.

forwardRef with class components

Although forwardRef works best with functional components, it can also be used in a class component. It comes in handy when using it with a library that uses forwardRef, when wrapping class components in higher-order components, accessing child component DOM nodes, or passing ref down through components.

Let’s look at instances where we have a class component and want to wrap it in a higher-order component while still being able to pass refs to the original class component. This can be achieved using forwardRef:

import React, { forwardRef, Component } from 'react';

class ButtonComponent extends Component {
  handleClick = () => {
    console.log('Button clicked in ButtonComponent');
  };

  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

const Form = forwardRef((props, ref) => (
  <ButtonComponent ref={ref} {...props} />
));

export default Form;

In the above example, the parent component can access the methods and properties of the ButtonComponent by passing the ref from the parent to the Form, which is then forwarded to the ButtonComponent:

import React, { useRef } from 'react';
import Form from './Form';

const App = () => {
  const buttonComponentRef = useRef();

  const handleButtonClick = () => {
    if (buttonComponentRef.current) {
      buttonComponentRef.current.handleClick(); // Call method in ButtonComponent
    }
  };

  return (
    <div>
      <Form ref={buttonComponentRef} />
      <button onClick={handleButtonClick}>click</button>
    </div>
  );
};

export default App;

You can wrap a class component in another class component and handle ref forwarding manually, but it’s more difficult and less intuitive than using forwardRef with a functional component.

You should also note that using forwardRef with class components can be complex in large codebases. It is important to use it carefully and comment on its usage clearly. Also note that debugging may become slightly more difficult when using forwardRef, as the component name may not appear as expected in React DevTools. You can address this by giving the forwarded component a display name.

Using useImperativeHandle with forwardRef

useImperativeHandle is a React Hook that lets you customize the instance value that is exposed when refs are used. It works well with forwardRef to expose imperative methods, which allows for more control and functionality.

The useImperativeHandle Hook can be useful when needed to expose methods or properties such as focus, toggle, mount, onClick, or custom methods of a component to the parent while keeping the component’s implementation details encapsulated.

useImperativeHandle is used within a component wrapped with forwardRef and it takes three arguments:

  • The ref to customize
  • A function that returns an object containing imperative methods
  • An array of optional dependencies

Let’s see how to use useImperativeHandle in React and how it offers enhanced component control:

import React, { useImperativeHandle, forwardRef, useRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    clear: () => {
      inputRef.current.value = '';
    },
  }));

  return <input ref={inputRef} {...props} />;
});

const App = () => {
  const inputRef = useRef();

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
      <button onClick={() => inputRef.current.clear()}>Clear Input</button>
    </div>
  );
};

export default App;

In the example code above, the App component can focus and clear the input field’s focus state using the exposed methods, providing precise control over the child component

useImperativeHandle works similarly to how useEffect works by allowing you to maximize speed by only recreating the imperative methods when certain dependencies change. This keeps the methods exposed to the parent component current and removes the need for pointless re-renders.

Let’s see that in action. We will create a component that exposes methods to toggle a modal’s visibility, which only re-creates the methods when the modal’s state changes:

import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react';

const Modal = forwardRef((props, ref) => {
  const [isVisible, setIsVisible] = useState(false);

  useImperativeHandle(ref, () => ({
    open: () => setIsVisible(true),
    close: () => setIsVisible(false),
    toggle: () => setIsVisible(prev => !prev),
  }), [isVisible]);

  return (
    <>
      {isVisible && (
          <div className="modal">
            <p>Modal Content</p>
            <button onClick={() => setIsVisible(false)}>Close</button>
          </div>
        )
      }
    </>
  );
});

const App = () => {
  const modalRef = useRef();

  return (
    <div>
      <button onClick={() => modalRef.current.open()}>Open Modal</button>
      <button onClick={() => modalRef.current.close()}>Close Modal</button>
      <button onClick={() => modalRef.current.toggle()}>Toggle Modal</button>
      <Modal ref={modalRef} />
    </div>
  );
};

export default App;

In this example, the imperative methods for managing the modal are only re-created when the isVisible state changes, making sure the parent component always contains the relevant methods without additional re-renders.

How to use forwardRef with TypeScript

TypeScript is a JavaScript subset that offers the benefits of static typing, enhanced tooling, and improved maintainability, leading to better and more reliable code in your JavaScript applications. forwardRef, as part of the React library, provides full support for TypeScript, though to maximize its benefits, the code we write should also be strongly typed.

Say, for example, that you have a functional component that uses forwardRef to expose the DOM reference to an HTML input element. This functional component also has its own props declared with their types. When using forwardRef in such an example, we’ll have to strongly type the component to avoid errors and improve code readability. One way to do this is by using generic types in forwardRef. Here’s an example:

type IInputProps = {
  label: string;
};

const InputText = React.forwardRef<HTMLInputElement, IInputProps>((props, ref) => (
    <div>
      <span>{props.label}</span>
      <input ref={ref} placeholder="your input goes here..." />
    </div>
  )
);

As we can see in the code, it is important to specify the type of the Ref element and the props. Another common way to declare the same component is to assign the type directly in the parameters of the callback function, as follows:

type IInputProps = {
  label: string;
};

const InputText = React.forwardRef((props: IInputProps, ref: React.Ref<HTMLInputElement>) => (
    <div>
      <span>{props.label}</span>
      <input ref={ref} placeholder="your input goes here..." />
    </div>
  )
);

In this case, when passing the type for the ref param, we need to make sure we are wrapping the element type with React.Ref. Both ways to declare the component are valid, and there is no argument for one over the other. It’s up to the developer’s style, and in my case, I prefer the first way because I believe it looks cleaner.

Similarly, when working on the parent component, we need to specify the reference type, and it needs to match the one used in forwardRef. To do that, you can use generic types when using useRef as follows:

const ref = React.useRef<HTMLInputElement>(null);

Failing to do so may trigger errors when trying to use methods and properties from the element, as we can see in the image below:

Forwardref Error Example In React

When not to use refs in React

In React, refs are a powerful feature that allows developers to interact with DOM elements and components directly. However, there are certain situations where using refs may not be the best approach. Here are a few:

Unnecessary DOM manipulation

React encourages a declarative approach to building UIs, so you should avoid using refs for direct DOM manipulation unless necessary. Use component state and props to handle most UI updates.

Overusing refs in stateless components

Functional components are often meant to be simple and stateless. If you find yourself using multiple refs in a functional component, consider if it could be split into smaller components or if state management should be lifted to a higher-level component.

Using refs for data flow

Refs should not be used as a replacement for state management or prop passing. Data should primarily flow through component props, and when necessary, state management libraries like Redux or React’s Context API can be used.

Using refs in place of controlled components

In form elements, use controlled components (by setting the value and handling input changes through state and event handlers) whenever possible. Refs should only be used for uncontrolled components when there is a specific need for direct access to the DOM element.

Accessing child components’ internal state

Refs should not be used to reach into a child component’s internal state or methods. Instead, use callback functions or other state management patterns to communicate between parent and child components.

Remember, refs should generally be used only when necessary. In many cases, React’s inbuilt mechanisms for state and prop management are more appropriate for handling component interaction and updates.

Comparing forwardRef, useRef, and Other Alternatives

When managing refs in React, it’s important to know when to use forwardRef, useRef, or other techniques. Each serves different use cases for accessing or passing references:

  • useRef — Use this hook when you want to create a mutable ref inside a functional component, typically to directly access a DOM element or keep a value between renders. However, useRef is local to the component where it’s declared and cannot be passed down through props to children

  • forwardRef — Use forwardRef when you need to pass a ref from a parent component through a child component to a nested DOM element or class instance. It’s essential when the child component itself doesn’t accept refs by default, such as function components wrapped in higher-order components

  • Other alternatives:

    • Callback refs — These give you more control by letting you set refs dynamically but can be more verbose

    • React Context — Sometimes refs or values can be shared using context for deeply nested components, but this is less direct and typically not used for DOM access

    • Imperative handle with useImperativeHandle — This works together with forwardRef to customize the instance value exposed to the parent

Choosing the right approach depends on your component hierarchy and whether you want direct DOM access or need to abstract it.

React 19 changes and what might replace forwardRef

In React 19, forwardRef is deprecated for function components. This reflects a shift in best practices toward alternative patterns for managing refs and component interaction.

While the official replacement is still evolving, some emerging approaches include:

  • Using hooks combined with context or state management to avoid direct DOM manipulations

  • Custom hooks that expose imperative methods, replacing the need to forward refs explicitly

  • Component APIs designed to handle interactions declaratively, reducing reliance on refs

Stay tuned to React’s official releases and RFCs to adopt the best modern alternatives as React evolves beyond version 18.

Conclusion

Refs in React provide a direct way to access DOM nodes or component instances, unlocking powerful options for building cleaner, more performant, and feature-rich components. But it’s important to remember: direct DOM manipulation is generally discouraged in React. When misused, refs can introduce bugs, complexity, and break React’s declarative model.

Use refs—and especially forwardRef — only when necessary, such as integrating with third-party libraries or managing focus imperatively. This article focused on how to use forwardRef in React 18 and earlier, explaining key use cases and practical examples with both function and class components.

Thanks for reading! Keep ref usage thoughtful and intentional.

The post React <code> forwardRef </code> explained: Usage, alternatives, and React 19 update appeared first on LogRocket Blog.

 

This post first appeared on Read More