This error occurs when attempting to access a forwarded ref inside a callback or useEffect hook before the ref has been populated with the DOM element. React refs are assigned after the component mounts, causing timing issues when accessed too early.
This error happens due to the timing of when React assigns values to refs. When you use `forwardRef` to expose a DOM node or imperative handle from a child component, the ref's `current` property starts as `null` and only gets populated after the component has mounted and the DOM element is created. If you try to access the ref inside a `useEffect` hook or callback function that runs before or during the initial render, the ref will still be `null`. This is especially common when using `forwardRef` with `useImperativeHandle`, where the exposed methods aren't available until after the child component has fully rendered. The issue is compounded when the ref is passed to a custom component that conditionally renders its content, defers rendering, or waits for user interaction before showing the underlying DOM element.
Always check if the ref is populated before accessing its properties or methods:
import { useEffect, useRef, forwardRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
// Child implementation
return <input ref={ref} />;
});
function ParentComponent() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// ❌ BAD: Accessing without null check
// inputRef.current.focus();
// ✅ GOOD: Check if ref is populated
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return <ChildComponent ref={inputRef} />;
}This prevents errors when the ref hasn't been assigned yet.
Callback refs are invoked when the DOM node is attached, guaranteeing the element exists:
function ParentComponent() {
const inputCallbackRef = (element: HTMLInputElement | null) => {
// This runs when the element is mounted
if (element) {
element.focus();
console.log('Element is ready:', element);
}
};
return <ChildComponent ref={inputCallbackRef} />;
}Callback refs are bound to the DOM node's lifecycle, not the component's mount cycle, making them more reliable for immediate operations.
If the parent doesn't provide a ref, the forwarded ref will be null:
const ChildComponent = forwardRef<HTMLInputElement, {}>((props, ref) => {
return <input ref={ref} />;
});
function ParentComponent() {
// ❌ BAD: No ref passed
// return <ChildComponent />;
// ✅ GOOD: Pass a ref
const inputRef = useRef<HTMLInputElement>(null);
return <ChildComponent ref={inputRef} />;
}Always verify the parent component is creating and passing a ref to the child.
When creating reusable components with forwardRef, account for cases where no ref is passed:
import { useRef, forwardRef, useImperativeHandle, useEffect } from 'react';
interface ChildMethods {
focus: () => void;
}
const ChildComponent = forwardRef<ChildMethods, {}>((props, forwardedRef) => {
const internalRef = useRef<HTMLInputElement>(null);
useImperativeHandle(forwardedRef, () => ({
focus: () => {
internalRef.current?.focus();
},
}));
useEffect(() => {
// Safe to use internal ref
if (internalRef.current) {
console.log('Internal ref is ready');
}
}, []);
return <input ref={internalRef} />;
});Using an internal ref ensures the component works even if forwardedRef is null.
Instead of accessing refs in useEffect, use them in response to user events:
function ParentComponent() {
const inputRef = useRef<HTMLInputElement>(null);
const handleButtonClick = () => {
// By the time user clicks, component is mounted and ref is populated
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<>
<ChildComponent ref={inputRef} />
<button onClick={handleButtonClick}>Focus Input</button>
</>
);
}Event handlers run after the component has mounted, ensuring refs are available.
React 19 Changes: In React 19, forwardRef is being deprecated in favor of passing ref as a regular prop. This simplifies the API but doesn't change the timing of when refs are populated.
Combining Internal and Forwarded Refs: You can merge multiple refs using a utility function or libraries like react-merge-refs. This is useful when you need both an internal ref for component logic and want to expose the element to parent components.
Conditional Rendering Gotchas: If the child component that receives the forwarded ref only renders conditionally (e.g., behind a loading state or modal), the ref will remain null until that condition becomes true. Consider exposing loading state to parent components so they know when the ref will be available.
TypeScript Considerations: When using TypeScript with forwardRef, properly type both the ref and props generics: forwardRef<RefType, PropsType>. This provides better autocomplete and catches errors where the wrong ref type is passed.
Performance: Callback refs are called on every render where the callback function changes. Use useCallback to memoize callback refs if you're performing expensive operations or want to prevent unnecessary re-invocations.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite