React utility to merge multiple refs. Fully correct behavior, matching React 19.
npm install react-best-merge-refsReact 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.
``console`
$ npm install react-best-merge-refs
`tsx
import type { Ref } from 'react';
import { useMergeRefs } from 'react-best-merge-refs';
function AutoFocusedInput({ ref }: { ref?: Ref
// Wrap our ref function in useCallback to make it stable across renders.
const autoFocusRef = useCallback((input: HTMLInputElement | null) => {
input?.focus();
}, []);
return (
// Even if ref might be unstable (since we don't control the consumer of this component),autoFocusRef
// our is guaranteed to only be called once per mount, no spurious calls on`
// re-render.
ref={useMergeRefs({ ref, autoFocusRef })}
/>
);
}
Low-level UI components often need to use their own refs, while also fowarding external ones, like this:
`tsx
import type { Ref } from 'react';
function MyInput({ ref }: { ref?: Ref
const inputRef = useRef
/ ... do stuff with inputRef ... /
// How can we pass both inputRef and the external ref here?`
return ... /} />;
}
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:
`tsx
import type { Ref } from 'react';
import { useMergeRefs } from 'react-best-merge-refs';
function MyInput({ ref }: { ref?: Ref
const inputRef = useRef
/ ... do stuff with inputRef ... /
return ;
}
`
The advantage of using this library is that we will keep compatibility with React semantics for you.
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!
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:
`tsx`
function BuggyComponent({
ref,
onDomReady,
}: {
ref?: Ref
onDomReadyCallbacks: (() => {})[];
}) {
return ;
}
...if a callback is added or removed in onDomReadyCallbacks, refs will be considered as having changed, includingref
calling 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 keyskey` prop).
as a last resort (again, like React's
By forcing all refs to be keyed (and thus explicitly unordered) we can safely track additions and removals without
breaking external refs by mistake.
Copyright 2025 Álvaro Cuesta (alvaro-cuesta@GitHub)
Licensed under the MIT license.