Combining Refs in React: Forwarding and Merging Made Easy
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
refdown viaforwardRef, so it can access the element. - Inside your component, you also need a
refto 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>:
That works fine. The parent can do:
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:
…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:
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:
Now, inside our button:
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.