This error occurs when you try to access ref.current during the render phase of a component using forwardRef, before React has attached the ref to a DOM node. Refs are only populated after the component mounts and the DOM element is created.
Fixes Cannot access ref.current in forwardRef before component renders
import React, { forwardRef, useEffect } from 'react';
const MyInput = forwardRef<HTMLInputElement>((props, ref) => {
// ❌ WRONG: Accessing ref.current during render
// console.log(ref.current); // Error!
useEffect(() => {
// ✅ CORRECT: Access ref.current after render
if (ref && typeof ref !== 'function' && ref.current) {
console.log('Input element:', ref.current);
ref.current.focus(); // Safe to call DOM methods
}
}, [ref]);
return <input ref={ref} {...props} />;
});import React, { forwardRef, useEffect } from 'react';const MyInput = forwardRef<HTMLInputElement>((props, ref) => {// ❌ WRONG: Accessing ref.current during render// console.log(ref.current); // Error!useEffect(() => {// ✅ CORRECT: Access ref.current after renderif (ref && typeof ref !== 'function' && ref.current) {console.log('Input element:', ref.current);ref.current.focus(); // Safe to call DOM methods}}, [ref]);return <input ref={ref} {...props} />;});Cannot access ref.current in forwardRef before component renders
This error happens when you attempt to read or write `ref.current` during the render phase of a React component that uses `forwardRef`. React refs follow a specific lifecycle: they are `null` or `undefined` until after the component has rendered and React has attached the ref to the actual DOM node. When using `forwardRef`, the ref parameter is passed to your component, but it won't have a `.current` value until React completes the render and commit phases. Accessing `ref.current` during rendering breaks React's expectation that components behave like pure functions, making the component's output unpredictable. React cannot track changes to `ref.current` during rendering, which means it won't know when to re-render your component. This is by design - refs are meant for accessing imperative DOM APIs or storing mutable values that don't affect rendering.
The most common fix is to move any ref.current access into a useEffect hook, which runs after the component renders and the ref is attached:
import React, { forwardRef, useEffect } from 'react';
const MyInput = forwardRef<HTMLInputElement>((props, ref) => {
// ❌ WRONG: Accessing ref.current during render
// console.log(ref.current); // Error!
useEffect(() => {
// ✅ CORRECT: Access ref.current after render
if (ref && typeof ref !== 'function' && ref.current) {
console.log('Input element:', ref.current);
ref.current.focus(); // Safe to call DOM methods
}
}, [ref]);
return <input ref={ref} {...props} />;
});For immediate DOM manipulation before paint, use useLayoutEffect instead of useEffect.
Always check that the ref exists and has a .current property before accessing it:
import React, { forwardRef, useEffect } from 'react';
const CustomComponent = forwardRef<HTMLDivElement>((props, ref) => {
useEffect(() => {
// Check if ref is an object with current property
if (ref && typeof ref !== 'function' && ref.current) {
// Safe to access ref.current here
const element = ref.current;
element.style.backgroundColor = 'lightblue';
}
}, [ref]);
return <div ref={ref}>Content</div>;
});TypeScript users can use optional chaining for cleaner code: ref?.current.
If you need to read DOM properties (like dimensions) before the browser paints, use useLayoutEffect:
import React, { forwardRef, useLayoutEffect, useState } from 'react';
const MeasuredComponent = forwardRef<HTMLDivElement>((props, ref) => {
const [height, setHeight] = useState(0);
useLayoutEffect(() => {
if (ref && typeof ref !== 'function' && ref.current) {
// Measure DOM before browser paints
const rect = ref.current.getBoundingClientRect();
setHeight(rect.height);
}
}, [ref]);
return (
<div ref={ref}>
Height: {height}px
{props.children}
</div>
);
});Note: useLayoutEffect blocks browser painting, so use it only when necessary.
When the element attached to the ref is conditionally rendered, the ref may remain null even inside effects:
import React, { forwardRef, useEffect } from 'react';
interface Props {
show: boolean;
}
const ConditionalInput = forwardRef<HTMLInputElement, Props>(
({ show }, ref) => {
useEffect(() => {
if (ref && typeof ref !== 'function' && ref.current) {
ref.current.focus();
}
// ref.current will be null if show is false
}, [ref, show]); // Include show in dependencies
// Only render input when show is true
if (!show) return null;
return <input ref={ref} placeholder="Type here..." />;
}
);The ref will only have a .current value when the element is actually rendered.
Callback refs give you more control over when and how refs are set:
import React, { forwardRef, useCallback } from 'react';
const SmartInput = forwardRef<HTMLInputElement>((props, forwardedRef) => {
const handleRef = useCallback(
(node: HTMLInputElement | null) => {
// Called when element mounts
if (node) {
console.log('Element mounted:', node);
node.focus();
}
// Forward the ref to parent
if (typeof forwardedRef === 'function') {
forwardedRef(node);
} else if (forwardedRef) {
forwardedRef.current = node;
}
},
[forwardedRef]
);
return <input ref={handleRef} {...props} />;
});Callback refs are called immediately when the element is attached or detached.
React 19 Update: In React 19, forwardRef is no longer necessary. You can pass ref as a regular prop:
// React 19
function MyInput({ ref }) {
return <input ref={ref} />;
}Fallback Ref Pattern: If you want a component to have its own internal ref even when no ref is forwarded, use a fallback pattern:
import React, { forwardRef, useRef } from 'react';
const ComponentWithFallback = forwardRef<HTMLDivElement>((props, forwardedRef) => {
const fallbackRef = useRef<HTMLDivElement>(null);
const ref = forwardedRef || fallbackRef;
// Now ref will always have a value, even if parent doesn't pass one
return <div ref={ref as React.RefObject<HTMLDivElement>}>Content</div>;
});useImperativeHandle: When using forwardRef with useImperativeHandle, remember that the imperative methods won't be available until after the component mounts:
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
interface CustomHandle {
focus: () => void;
}
const CustomInput = forwardRef<CustomHandle>((props, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current?.focus();
},
}));
return <input ref={inputRef} />;
});
// Parent usage
function Parent() {
const ref = useRef<CustomHandle>(null);
useEffect(() => {
// Safe to call after mount
ref.current?.focus();
}, []);
return <CustomInput ref={ref} />;
}Performance Consideration: Accessing refs doesn't trigger re-renders, which is why you can't use ref.current as a dependency in rendering logic. If you need a value that affects rendering, use useState instead.