Skip to main content

Usage

import { useMergeRefs } from "@servicetitan/anvil2";
import { useRef, forwardRef } from "react";

const CustomInput = forwardRef((props, forwardedRef) => {
  const internalRef = useRef(null);
  const mergedRef = useMergeRefs([internalRef, forwardedRef]);

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

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

Parameters

ParameterTypeDescription
refsArray<Ref<T> | undefined>Array of React refs to merge

Return Value

Returns a callback ref that handles all provided refs, or null if all refs are null/undefined.

Features

  • Handles both function refs and object refs
  • Optimized with useMemo for performance
  • Compatible with floating-ui patterns

Examples

Forwarding Refs with Internal Usage

const FocusableButton = forwardRef((props, forwardedRef) => {
  const buttonRef = useRef(null);
  const mergedRef = useMergeRefs([buttonRef, forwardedRef]);

  useEffect(() => {
    // Can use buttonRef internally
    if (props.autoFocus) {
      buttonRef.current?.focus();
    }
  }, [props.autoFocus]);

  // Parent can also access via forwardedRef
  return <button ref={mergedRef} {...props} />;
});

// Usage
const parentRef = useRef(null);
<FocusableButton ref={parentRef} autoFocus />;

Multiple Internal Refs

function ComplexComponent() {
  const measureRef = useRef(null);
  const animationRef = useRef(null);
  const scrollRef = useRef(null);

  const mergedRef = useMergeRefs([measureRef, animationRef, scrollRef]);

  return <div ref={mergedRef}>Content</div>;
}

Conditional Refs

function ConditionalRefComponent({ shouldMeasure }) {
  const measureRef = useRef(null);
  const baseRef = useRef(null);

  // Only include measureRef when needed
  const mergedRef = useMergeRefs([
    baseRef,
    shouldMeasure ? measureRef : undefined,
  ]);

  return <div ref={mergedRef}>Content</div>;
}
This hook is similar to the useMergeRefs hook from floating-ui and follows the same patterns.
Last modified on January 23, 2026