react-best-merge-refs
v1.0.2
Published
React utility to merge multiple refs. Fully correct behavior, matching React 19.
Downloads
158
Maintainers
Readme
react-best-merge-refs 🔗
React utility to merge multiple refs into a single one.
Fully correct behavior, matching React 19. See "Why would I need this?" for an explanation, and "Why not other ref merging libraries?" for additional details on what other ref merging libraries get wrong.
$ npm install react-best-merge-refsExample
import type { Ref } from 'react';
import { useMergeRefs } from 'react-best-merge-refs';
function AutoFocusedInput({ ref }: { ref?: Ref<HTMLInputElement> }) {
// Wrap our ref function in `useCallback` to make it stable across renders.
const autoFocusRef = useCallback((input: HTMLInputElement | null) => {
input?.focus();
}, []);
return (
<input
// Even if `ref` might be unstable (since we don't control the consumer of this component),
// our `autoFocusRef` is guaranteed to only be called once per mount, no spurious calls on
// re-render.
ref={useMergeRefs({ ref, autoFocusRef })}
/>
);
}FAQ
Why would I need this?
Low-level UI components often need to use their own refs, while also fowarding external ones, like this:
import type { Ref } from 'react';
function MyInput({ ref }: { ref?: Ref<HTMLInputElement> }) {
const inputRef = useRef<HTMLInputElement>(null);
/* ... do stuff with `inputRef` ... */
// How can we pass both `inputRef` and the external `ref` here?
return <input ref={/* ... */} />;
}React does not offer a way to set two refs inside the ref property (see facebook/react#29757). This small utility
allow you to combine multiple refs into a single ref that you can pass around like this:
import type { Ref } from 'react';
import { useMergeRefs } from 'react-best-merge-refs';
function MyInput({ ref }: { ref?: Ref<HTMLInputElement> }) {
const inputRef = useRef<HTMLInputElement>(null);
/* ... do stuff with `inputRef` ... */
return <input ref={useMergeRefs({ ref, inputRef })} />;
}The advantage of using this library is that we will keep compatibility with React semantics for you.
Why not other ref merging libraries?
React 19 introduced cleanup functions for refs and this library has full support for them, completely providing the same guarantees that React provides for you, exactly as if you were using individual refs.
React refs are subtle and merging is easy to implement wrong. The fantastic react-merge-refs library is a great implementation, but has a subtle issue with unstable refs that (1) completely breaks React's contract for refs and, worst of it, (2) cannot be fixed without patching the broken library (your refs are subtly broken as long as anyone merges them with unstable refs).
See "Example" for a very common use case that is broken in other libraries. This pattern is so common across the whole React ecosystem, I'm pretty sure most merged refs are subtly broken everywhere!
Why an object for refs instead of a simple array?
Other merging libraries allow you to pass the refs as an array (or multiple arguments), but this can lead to subtle bugs under some circumstances since the refs are implicitly ordered.
Similar to React's key prop we need to track individual refs over time (regardless of their order) to abide to React's ref semantics. For example, if we allowed an array and someone compiled a list of refs like this:
function BuggyComponent({
ref,
onDomReady,
}: {
ref?: Ref<HTMLInputElement>;
onDomReadyCallbacks: (() => {})[];
}) {
return <input ref={useMergeRefs([...onDomReadyCallbacks, ref])} />;
}...if a callback is added or removed in onDomReadyCallbacks, refs will be considered as having changed, including
calling ref unexpectedly and breaking React's guarantees for anyone using your component.
Since merging refs can break the contract even for refs that are external to
your code, it is very important to keep the API foolproof and prevent doing the wrong thing. You probably have some sort
of stable id (like the one you'd use for a key prop) and if you don't you can always resort to using indices as keys
as a last resort (again, like React's key prop).
By forcing all refs to be keyed (and thus explicitly unordered) we can safely track additions and removals without breaking external refs by mistake.
License (MIT)
Copyright 2025 Álvaro Cuesta (alvaro-cuesta@GitHub)
Licensed under the MIT license.
