Common React Ref Errors and How to Fix Them

Diagnose the common React ref errors, from null current values to unforwarded refs and conditional nodes, with a fix for each.

7 min read

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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
  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

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.
RunePowered by Rune AI

Frequently Asked Questions

Why is ref.current null in my component?

React fills ref.current during the commit phase, after the DOM node exists. On the first render it is null, and it is null when the node is not rendered.

Why does reading ref.current during render cause bugs?

Render output should depend only on props, state, and context. A ref changes silently, so reading it during render makes output unpredictable.

How do I create refs for items in a list?

Do not call useRef inside map. Use a callback ref that stores each node in a Map, or a single ref on the list container.

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.