useThreadSafeRef
The useThreadSafeRef hook keeps a ref pointing at the last mounted DOM node, even when the element is unmounted and remounted by conditional rendering. It does this by only ever writing a truthy node into the ref — React's own detach call (ref(null)) is ignored, so current never flickers back to null mid-lifecycle. More Info...
import { useThreadSafeRef } from '@archibald/client';
const [setRef, currentRef] = useThreadSafeRef();
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
ref | RefObject<HTMLElement | undefined> | An existing object ref to write into. If omitted, the hook creates and manages an internal one via useRef. |
The parameter is an object ref (the result of useRef), not a React.Ref. Callback refs and null-initialised refs are not supported:
useThreadSafeRef(useRef<HTMLButtonElement>(null))fails to compile —RefObject<HTMLButtonElement | null>is not assignable toRefObject<HTMLElement | undefined>. Declare the ref asuseRef<HTMLElement | undefined>(undefined)instead.- A callback ref is never invoked.
setRefonly assignscurrentRef.current = node, so passing a function would set a property on that function and the parent's callback would never fire.
The hook also does not merge refs: when you pass one in, it becomes the single ref the hook writes to, replacing the internal one rather than mirroring into both.
Return Value
A readonly tuple of exactly two elements:
setRef— a stable callback ref to attach to your element via therefprop.currentRef— the object ref holding the node in.current(HTMLElement | null).
Because .current is typed as HTMLElement, shared APIs such as focus() and offsetHeight are available directly; element-specific members need a cast:
currentRef.current?.focus(); // ok — HTMLElement
(currentRef.current as HTMLInputElement | null)?.value; // cast needed
Practical Code Example
Internal ref
Attach setRef and read the node from currentRef — no argument, no ref plumbing from the caller:
import { useThreadSafeRef } from '@archibald/client';
function MyComponent() {
const [setRef, currentRef] = useThreadSafeRef();
const handleClick = () => {
console.log('DOM node:', currentRef.current);
};
return (
<div ref={setRef} onClick={handleClick}>
Click Me
</div>
);
}
Ref supplied by the parent
Declare the prop with the type the hook accepts and pass the ref straight through. React 19 treats ref as an ordinary prop, so no forwardRef wrapper is involved:
import { useThreadSafeRef } from '@archibald/client';
import { useRef, type RefObject } from 'react';
function Child({ ref }: { ref?: RefObject<HTMLElement | undefined> }) {
// Writes into the parent's ref instead of an internal one.
const [setRef] = useThreadSafeRef(ref);
return <div ref={setRef}>I am using the parent's ref!</div>;
}
function Parent() {
// Must be `HTMLElement | undefined`. `useRef<HTMLDivElement>(null)` does not typecheck.
const externalRef = useRef<HTMLElement | undefined>(undefined);
return <Child ref={externalRef} />;
}
This is a framework-level utility: no component in the basic or shop template consumes it today. Reach for it when you are writing a reusable component that must work whether or not a ref is handed in, and where the element may unmount and remount.