Go Back

Combining Refs in React: Forwarding and Merging Made Easy

Posted: 
Last modified: 

React’s ref system is a powerful way to directly interact with DOM elements or imperatively control components. But sometimes, you run into a situation where you need to use more than one ref on the same element.

For example:

  • Your parent component passes a ref down via forwardRef, so it can access the element.
  • Inside your component, you also need a ref to that same element—for event listeners, animations, or measuring size.

By default, React only lets you attach one ref to a DOM node. So how do we merge multiple refs together? Let’s walk through it.


The Problem

Here’s a button component that forwards a ref to its underlying <button>:

import React, { forwardRef } from "react";

const MyButton = forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
(props, ref) => {
return <button ref={ref} {...props} />;
}
);

That works fine. The parent can do:

const parentRef = useRef<HTMLButtonElement>(null);

<MyButton ref={parentRef}>Click Me</MyButton>;

But what if the button itself also needs its own ref—for example, to manage focus styles or measure its size?

If we simply write:

const innerRef = useRef<HTMLButtonElement>(null);

<button ref={innerRef} {...props} />

…we lose the parent’s ref, because React overwrites ref when we set a new one.


The Solution: Ref Callback

Instead of assigning a ref directly, we can use a ref callback. This function gets called whenever React attaches or detaches the element. Inside it, we can set both refs:

const MyButton = forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
(props, ref) => {
const innerRef = React.useRef<HTMLButtonElement>(null);

function setRefs(el: HTMLButtonElement | null) {
innerRef.current = el;

if (typeof ref === "function") {
ref(el);
} else if (ref) {
(ref as React.MutableRefObject<HTMLButtonElement | null>).current = el;
}
}

return <button ref={setRefs} {...props} />;
}
);

Now both innerRef and the parent’s ref point to the same <button> element. 🎉


Extracting a useMergeRefs Hook

That setRefs function is reusable! Let’s wrap it into a custom hook:

function useMergeRefs<T>(
...refs: (React.Ref<T> | undefined)[]
): React.RefCallback<T> {
return React.useCallback(
(value: T | null) => {
for (const ref of refs) {
if (!ref) continue;

if (typeof ref === "function") {
ref(value);
} else {
(ref as React.MutableRefObject<T | null>).current = value;
}
}
},
[refs]
); }

Now, inside our button:

const MyButton = forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
(props, ref) => {
const innerRef = React.useRef<HTMLButtonElement>(null);

const mergedRef = useMergeRefs(ref, innerRef);

return <button ref={mergedRef} {...props} />;
}
);


Why This Matters

This pattern is especially useful for:

  • Reusable UI components (buttons, inputs, modals) where parents often want access to the DOM.
  • Libraries like animation hooks or toolkits that need internal refs but don’t want to break external ref usage.
  • Accessibility patterns that require measuring or focusing DOM elements.

By merging refs, you give both your component and its consumers full access without conflicts.


Final Thoughts

Refs are an advanced but essential tool in React. When you need to forward a ref but also use your own, ref callbacks (and a useMergeRefs hook) make it clean and reusable.

Once you start building complex components, you’ll find this pattern comes up a lot—and with a hook like useMergeRefs, you’ll never have to rewrite it again.