Ref errors usually share one symptom: ref.current is null or undefined at the moment you read it. The fixes are small once you know whether the problem is timing, a missing forward, or a conditional element. Most failures show up as a TypeError in the console or as a screen that never responds to focus. Each section below shows the symptom, the cause, and the smallest fix.
Error: Cannot read properties of null
Reading current before commit throws because the node does not exist yet. This happens when you read a ref during render or before React attaches it.
function Input() {
const inputRef = useRef(null);
inputRef.current.focus(); // null during render
return <input ref={inputRef} />;
}The render body runs before the input exists, so focus throws. Move the action to an Effect, which runs after commit.
import { useRef, useEffect } from "react";
function Input() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}Now the field focuses after it appears. Event handlers are also safe because they run after commit. Reading a ref inside an Effect is safe for the same reason, as long as the node has rendered.
Error: ref to a custom component is null
A ref only reaches a DOM node if the component forwards it. If the child ignores the ref prop, current stays null. This is the most common reason a ref stays null even though the component renders.
function MyInput({ ref }) {
return <input />;
}The child drops the ref, so the parent never gets the node. Attach the ref prop to the real element instead.
function MyInput({ ref }) {
return <input ref={ref} />;
}The parent's ref now points at the input. See how to pass refs between components for the full flow.
Error: reading or writing current during render
Render must stay pure, so it should not depend on a value that changes silently. A ref is not part of render's inputs.
function Counter() {
const countRef = useRef(0);
return (
<button onClick={() => (countRef.current += 1)}>
{countRef.current} clicks
</button>
);
}The label never updates because changing a ref does not re-render. Writing a ref during render has the same problem, because React cannot see the change. If the value appears on screen, store it in state instead.
Error: the node is conditionally rendered
When an element only renders under a condition, its ref is null while the condition is false.
import { useRef, useEffect } from "react";
function Panel({ open }) {
const panelRef = useRef(null);
useEffect(() => {
panelRef.current.focus();
}, [open]);
return open ? <div ref={panelRef} tabIndex={0}>Panel</div> : null;
}If open is false, the div is not in the DOM and current is null. Reading it inside the Effect throws, because the node has not been committed.
useEffect(() => {
if (panelRef.current) {
panelRef.current.focus();
}
}, [open]);The guard skips focus while the panel is hidden. When open turns true, the node commits and the next Effect run focuses it.
What to learn next
Refs for list items need a different approach. Continue with callback refs vs object refs to see the callback form in action.
Rune AI
Key Insights
- ref.current is null during render and until commit attaches the node.
- Custom components must forward the ref prop to a DOM node.
- Never read or write ref.current during render.
- A conditionally rendered node leaves its ref null when hidden.
- Use a callback ref for refs to list items.
Frequently Asked Questions
Why is ref.current null in my component?
Why does reading ref.current during render cause bugs?
How do I create refs for items in a list?
Conclusion
Most ref errors come down to timing, a missing forward, or a conditional node. Read refs after commit, forward them through custom components, and check for null when an element may not render.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.