This error occurs when accessing ref.current before the ref is initialized or when a forwarded ref is not properly set up. The ref object exists but its current property is null because the component hasn't mounted yet or the ref wasn't passed correctly through forwardRef.
This TypeError happens when you try to access the `current` property of a ref that is `null` rather than a valid ref object. This is particularly common when using React's `forwardRef` API, which allows parent components to access a child component's DOM node or instance. The error typically occurs in one of three scenarios: accessing the ref too early in the component lifecycle (before the component has mounted), forgetting to pass the ref prop down to a DOM element or component, or incorrectly implementing the forwardRef wrapper. When a ref is forwarded but not attached to anything, React initializes it as `null` rather than `undefined`, leading to this specific error message. Understanding the timing of ref initialization is crucial. Refs are only populated after the component mounts and the underlying DOM node is created. Attempting to access `ref.current` during render or in synchronous code outside of effects or event handlers will often result in this error.
Ensure you access ref.current only after the component has mounted, typically in a useEffect hook or event handler:
function ParentComponent() {
const childRef = useRef(null);
// ❌ Wrong: Accessing during render
// console.log(childRef.current); // null!
// ✅ Correct: Access in useEffect
useEffect(() => {
if (childRef.current) {
console.log(childRef.current); // Now available
childRef.current.focus();
}
}, []);
// ✅ Correct: Access in event handler
const handleClick = () => {
if (childRef.current) {
childRef.current.scrollIntoView();
}
};
return (
<>
<ChildComponent ref={childRef} />
<button onClick={handleClick}>Scroll to child</button>
</>
);
}Always use null checks (if (ref.current)) before accessing ref properties.
When creating a component that needs to expose a ref, wrap it with forwardRef and attach the ref to a DOM element:
import { forwardRef } from 'react';
// ❌ Wrong: Not forwarding ref
function MyInput(props) {
return <input {...props} />;
}
// ✅ Correct: Forwarding ref to DOM element
const MyInput = forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
// Usage
function Parent() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus(); // Safe access with optional chaining
}, []);
return <MyInput ref={inputRef} />;
}The second parameter of the forwardRef callback is the ref passed from the parent. You must attach it to a DOM element or pass it to another forwardRef component.
When you want to expose specific methods rather than the DOM element itself, use useImperativeHandle:
import { forwardRef, useRef, useImperativeHandle } from 'react';
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current?.focus();
},
scrollIntoView: () => {
inputRef.current?.scrollIntoView({ behavior: 'smooth' });
},
getValue: () => {
return inputRef.current?.value || '';
}
}), []); // Empty dependency array
return <input ref={inputRef} {...props} />;
});
// Usage
function Parent() {
const customRef = useRef(null);
const handleAction = () => {
customRef.current?.focus(); // Call exposed method
console.log(customRef.current?.getValue());
};
return (
<>
<CustomInput ref={customRef} />
<button onClick={handleAction}>Focus Input</button>
</>
);
}Always include null checks when calling imperative handle methods.
If your component may be conditionally rendered or unmounted, always check if the ref is still valid:
function Parent() {
const [show, setShow] = useState(true);
const childRef = useRef(null);
const handleInteraction = () => {
// ❌ Wrong: Ref might be null if component unmounted
// childRef.current.doSomething();
// ✅ Correct: Check before using
if (childRef.current) {
childRef.current.doSomething();
}
};
useEffect(() => {
// Only run effect if component is mounted
if (show && childRef.current) {
childRef.current.initialize();
}
// Cleanup when unmounting
return () => {
if (childRef.current) {
childRef.current.cleanup?.();
}
};
}, [show]);
return (
<>
{show && <Child ref={childRef} />}
<button onClick={() => setShow(!show)}>Toggle</button>
<button onClick={handleInteraction}>Interact</button>
</>
);
}Consider using optional chaining (ref.current?.method()) for cleaner null-safe access.
When working with third-party libraries that may not handle forwarded refs properly, create a fallback:
import { useState, useRef, forwardRef } from 'react';
const ComponentWithFallback = forwardRef((props, forwardedRef) => {
// Create fallback ref using lazy initialization
const [fallbackRef] = useState(() =>
forwardedRef || useRef(null)
);
const ref = forwardedRef || fallbackRef;
// Use ref normally
useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, [ref]);
return <input ref={ref} {...props} />;
});
// Alternative: Check ref type
const SafeComponent = forwardRef((props, forwardedRef) => {
const localRef = useRef(null);
// Use forwarded ref if valid, otherwise use local
const activeRef =
forwardedRef && typeof forwardedRef === 'object' && 'current' in forwardedRef
? forwardedRef
: localRef;
return <input ref={activeRef} {...props} />;
});This pattern prevents errors when refs are passed incorrectly or not at all.
React 19 Changes: Starting with React 19, ref is available as a regular prop, making forwardRef less necessary. The forwardRef API will be deprecated in future versions, so consider updating components to use ref as a prop when targeting React 19+.
TypeScript Considerations: When using TypeScript with refs, you may encounter "Object is possibly null" errors. Use type guards (if (ref.current)) or the non-null assertion operator (ref.current!) carefully. For better type safety, define the ref type explicitly: const ref = useRef<HTMLInputElement>(null).
Fast Refresh Issues: In Next.js and some development environments, fast refresh can occasionally cause refs to become empty objects instead of proper ref objects, leading to "can't define property 'current'" errors. This is typically resolved by doing a full page refresh rather than a fast refresh.
Ref Callback Pattern: For more control over ref timing, use the callback ref pattern: <div ref={(node) => { if (node) { /* safe to use */ } }} />. This callback is invoked immediately when the element is mounted and with null when unmounted.
Multiple Refs: When you need both a local ref and a forwarded ref, merge them using a callback: const setRefs = useCallback((node) => { localRef.current = node; if (forwardedRef) forwardedRef.current = node; }, [forwardedRef]), then use <div ref={setRefs} />.
Performance: Accessing ref.current repeatedly in render can cause issues. Cache the value in a variable within effects or handlers: const element = ref.current; if (element) { /* use element */ }.
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