Facebook Pixel

Using useRef for DOM Access

In this tutorial, we will learn exactly what useRef is used for in React 19 and when it is the correct tool to reach for. Many beginners misuse useRef as a replacement for state or as a way to bypass React’s data flow. By the end, you will understand the correct mental model for refs and how to use them safely and intentionally.

You will learn:

  • What useRef actually does
  • When you should use useRef
  • When not to use it
  • Common beginner mistakes and modern alternatives
  • How to think about refs in a React 19 world

What We Will Learn

  1. What useRef really is
  2. How refs differ from state
  3. Valid DOM access use cases
  4. Non DOM use cases for refs
  5. Common anti patterns
  6. The correct mental model for useRef

What useRef Really Is

useRef creates a persistent, mutable container that React keeps between renders.

const ref = useRef(initialValue);

That container looks like this:

{
  current: initialValue
}

React does not track changes to ref.current.

This is the most important rule:

Updating a ref does not cause a re render.


Why useRef Exists

React is declarative. The UI should be a function of data.

However, some things cannot be expressed declaratively, such as:

  • Focusing an input
  • Scrolling to an element
  • Measuring layout
  • Controlling media playback
  • Integrating with imperative browser APIs

useRef is React’s escape hatch for these cases.


When You SHOULD Use useRef

Below are the correct, modern use cases.


1. Accessing DOM Elements

This is the most common and intended use.

Example: Focusing an Input

import { useRef } from "react";
 
function FocusInput() {
  const inputRef = useRef(null);
 
  function focusInput() {
    inputRef.current.focus();
  }
 
  return (
    <>
      <title>useRef DOM Access</title>
      <meta
        name="description"
        content="Using useRef to access DOM elements in React 19"
      />
 
      <input ref={inputRef} placeholder="Type here..." />
      <button onClick={focusInput}>Focus Input</button>
    </>
  );
}

What is happening:

  • React renders the input
  • React assigns the DOM node to inputRef.current
  • You imperatively call a browser API

This is a valid use of useRef.


2. Reading Layout and Measurements

Some values only exist after the DOM is rendered.

import { useRef } from "react";
 
function BoxSize() {
  const boxRef = useRef(null);
 
  function logSize() {
    const box = boxRef.current;
    console.log(box.offsetWidth, box.offsetHeight);
  }
 
  return (
    <div>
      <div
        ref={boxRef}
        style={{ width: 200, height: 100, background: "lightgray" }}
      />
      <button onClick={logSize}>Log Size</button>
    </div>
  );
}

You are not controlling the UI with the ref. You are reading information from the DOM.

That is correct usage.


3. Persisting Values Across Renders

Refs can store any mutable value that should survive renders but should not trigger UI updates.

import { useRef } from "react";
 
function RenderCounter() {
  const renderCount = useRef(0);
  renderCount.current++;
 
  return <p>Rendered {renderCount.current} times</p>;
}

Why this works:

  • The ref persists across renders
  • Updating .current does not re render
  • React keeps the value for you

This is useful for timers, IDs, previous values, and counters.


4. Storing Imperative Handles

Refs are often used to store things like:

  • timers
  • animation instances
  • observers
  • media players

Example:

const intervalRef = useRef(null);
 
intervalRef.current = setInterval(() => {
  console.log("tick");
}, 1000);

Again, no UI logic involved.


When NOT To Use useRef

React 19 makes many old ref patterns unnecessary.

These are anti patterns.


1. DON’T Use useRef for UI State

Incorrect:

const count = useRef(0);
 
function increment() {
  count.current++;
}

Refs do not re render the UI.

If the value affects what the user sees, use state or server data.


2. DON’T Replace State with useRef

If you need the UI to update, refs are the wrong tool.

Bad idea:

const isOpen = useRef(false);

Correct idea:

const [isOpen, setIsOpen] = useState(false);

3. DON’T Read Refs During Render for Logic

Refs are imperative, not declarative.

Avoid:

if (inputRef.current) {
  // render logic
}

Rendering should depend on props, state, or server data, not refs.


4. DON’T Use Refs to Avoid React Patterns

Using refs to bypass React is a code smell.

If you find yourself mutating refs to “force” behavior, stop and rethink the design.


Common Beginner Mistakes

Forgetting .current

Incorrect:

inputRef.focus();

Correct:

inputRef.current.focus();

Assuming Refs Exist on First Render

Refs are null during the first render.

Only access them after React commits the DOM.


Overusing useRef

If your component has many refs controlling logic, it is likely doing too much imperatively.


A Helpful Mental Model

Think of useRef as:

A box that React holds for you, but does not look inside.

  • React will not re render when it changes
  • React will not track it
  • React will not optimize it

It is outside React’s reactive system.


useRef in the React 19 Mental Model

Use:

  • use() for async data
  • Actions for mutations
  • State for UI
  • JSX for metadata
  • useRef for imperative escape hatches

Each tool has a narrow, intentional purpose.


Summary Table

Task Use useRef? Why
Focusing an input Yes Imperative DOM access
Reading element size Yes DOM measurement
Persisting mutable values Yes No re render needed
Rendering UI values No Use state or data
Managing application state No Breaks React data flow
Fetching data No Use use() or Actions
Triggering UI updates No Refs do not re render

Conclusion

useRef is a powerful but narrow tool.

In React 19, you should use it only when you need to step outside React’s declarative model and interact with the DOM or other imperative systems. It is not a replacement for state, not a shortcut for logic, and not a way to avoid React patterns.

Used correctly, useRef keeps your components clean, predictable, and aligned with modern React best practices.