How to use React's forwardRef function

forwardRef is a helper function from React that allows us to forward a component's ref to another one.

This tutorial will teach what all of that means and how to use the function, including the correct TypeScript definitions.

What are refs in React again?

Before we start, let's do a quick recap of what refs (or references) in React are.

We will usually use references in React to store the reference to a DOM element, such as a button or an input field.

Having the element available to us as a variable in our code allows us to use browser APIs only available through the DOM.

Let's say we want to programmatically focus an input element when the user clicks a button. React doesn't have a built-in way of doing this, but in plain JavaScript, we would query the DOM to find the element first and then call the focus function that's part of the element.

const inputElement = document.getElementById('input-element-id');
inputElement.focus();

React makes these operations a bit easier by allowing us to store references to DOM elements like this:

const App = () => {
  const inputRef = useRef(null);
  return (
    <>
      <input
        ref={inputRef}        value={name}
        onChange={handleChange}
      />
      <button
        onClick={() => inputRef.current.focus()}
      >
        Focus
      </button>
    </>
  );
};

We don't need to assign an ID to our input element anymore, as we can pass down a ref variable to the DOM element. React will then assign the element to this variable.

The ref object will look like the following after React assigned the element.

{ current: input }

React stores the HTML element under the current property of the reference.

Make sure that you pass down a variable that has been initialized with useRef (or createRef in class components).

If we now want to focus input element, we just need to call inputRef.current.focus().

You can check out this codepen to try it out yourself.

If you want to learn more about references and the useRef hook, I recommend checking out my other article on the topic.

Why do we need forwardRef in React?

So what is forwardRef, and why is it important?

Let's start with an example: Let's say we have an Input component in our React application that looks like the following:

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

Now, we also want to focus the input component on the click of a button.

To achieve that, we simply need to create a new reference in our app, pass it down to Input, and call .focus() on the element, right? Wrong!

By default, the ref prop only works on HTML elements, not on React components.

When we want to pass down a reference to a React component, we need to tell React which HTML element it should reference, as there can be more than one in our component.

That's where forwardRef becomes useful. It allows us to specify which exact HTML element we want to reference.

How to use forwardRef

Our task is to forward the ref that the Input component receives to the HTML input element.

We do that by wrapping the component in forwardRef. The function accepts a callback with two parameters:

  1. The component props
  2. The ref to be forwarded

This callback is our function component, which now accepts a second property.

const Input = forwardRef((props, ref) => {
  // Here goes the content of our component
});

In the returned JSX code, we now need to pass the ref we receive in the function to the correct HTML component, which in our case is the input element.

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

Et voilá, focussing the input via refs works!

Here's the code for doing so:

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

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

const App = () => {
  const inputRef = useRef(null);
  const [value, setValue] = useState('');
  const onInputChange = (e) => {
    e.preventDefault();
    setValue(e.target.value);
  };

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

  return (
    <>
      <Input value={value} onChange={onInputChange} ref={inputRef} />
      <button onClick={focus}>Focus</button>
    </>
  );
};

You can try the code in this Codepen.

Custom names for DevTools

The forwardRef function takes the name of the component from the function it accepts as a parameter. If you're using anonymous or arrow functions, your DevTools will display the component like this:

React DevTools anonymous function forwardRef

There are a few ways to avoid this. First, you could use a regular function expression with a name like this:

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

If you're like me and haven't used this syntax in the last three years, you can also set the display name manually by adding the following line to the end of the file:

Input.displayName = 'Input';

Now we have covered the primary use-cases of forwardRef. Continue reading if you want to know how to use it with TypeScript, class components, or higher-order components.

How to use forwardRef with class components

Unfortunately, we can't just wrap our class components in forwardRef.

However, we can use our class components in the render function that we provide to forwardRef.

class Input extends React.Component {
  const { innerRef, ...props } = this.props;
  render() (
    return (
      <div style={{backgroundColor: "blue"}}>
        <input ref={innerRef} {...props} />
      </div>
    )
  )
}

const InputForwardingRef = forwardRef((props, ref) => (
  <Input {...props} innerRef={ref}/>));

The example is taken from this StackOverflow answer.

There's one crucial difference here: we can't use the ref prop as this will only work if the component uses forwardRef. We need to come up with an alternative name, like innerRef in this example.

Forwarding refs in higher-order components

As we have seen with standard React components, the ref prop is unique as we can only access it through the forwardRef function.

The same applies to higher-order components (HOCs) as well.

Let's say you have a HOC that passes all props through to the component it is wrapped around.

function logProps(WrappedComponent) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return LogProps;
}

This example was taken from the React documentation.

This function won't pass the ref prop to WrappedComponent, though. To handle this case as well, we need to make use of forwardRef again.

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;
      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  return React.forwardRef((props, ref) => {    return <LogProps {...props} forwardedRef={ref} />;  });}

If you want a more detailed explanation of how to use refs with higher-order components, I recommend you check out the React documentation, which does a great job explaining this topic.

How to use forwardRef with TypeScript

With TypeScript, we can define what type of reference we expect, which is very helpful, as an input element provides different functions than a button, for instance.

forwardRef accepts two generic types. The first one defines the type of the reference, while we can use the second one for the component props.

const Input = forwardRef<HTMLInputElement, IInputProps>((props, ref) => {
  return <input ref={ref} {...props} />;
});

Alternatively, we can also add the type definitions to the callback function.

The second argument needs to be of the type React.Ref, which is the generic type definition for references. We can then pass the type of the HTML element to the generic type (Ref<HTMLInputElement>).

const Input = forwardRef((props: IInputProps, ref: Ref<HTMLInputElement>) => {
  return <input ref={ref} {...props} />;
});

When using such a strongly-typed component, we need to ensure that the type of the ref that we pass down to the component matches the expected HTML element.

Without a proper type, we'll also run into issues when trying to access functions or properties of our ref, which will result in type errors.

React ref type error

Property 'focus' does not exist on type 'never'.

With useRef, we can define the type of the reference like this:

const inputRef = useRef<HTMLInputElement>(null);

useRef accpets one parameter.

Conclusion

As the ref prop doesn't behave like other props, it can be confusing when dealing with React's references, but once we understand how to use forwardRef, it becomes a lot easier.

I hope that this tutorial could answer all the questions you had about forwardRef and references. If not, you may check out my other article on using useRef or leave a comment if I'm missing something. Thanks for reading!

Further reading:

Support

PatreonKoFi