Back to Blog
Lesson 1 of the Intermediate React: Hooks, State & Data Patterns course
ReactJune 25, 20264 min read

Mastering useRef for DOM Access in React

Learn how to use the useRef hook to interact with DOM nodes and persist mutable values without triggering re-renders in your React applications.

ReactuseRefhooksDOMfrontendintermediatejavascript

Previously in this course, we explored how to manage component lifecycles in React lifecycle: Mastering Mounting, Updating, and Unmounting. While state is the primary way to drive your UI, sometimes you need to break out of that paradigm to talk directly to the browser.

useRef is your "escape hatch." It allows you to hold a reference to a DOM node or keep track of a value that doesn't need to trigger a re-render when it changes. In this lesson, we’ll move beyond theoretical state and start interacting with the browser's imperative DOM API.

Understanding useRef from First Principles

In React, we usually rely on state to update the DOM. When state changes, React re-renders the component and updates the DOM to match. However, there are times when you need to access a DOM element directly—like focusing an input, scrolling to a specific div, or integrating a third-party library that expects a raw DOM node.

A ref is a plain JavaScript object with a single property: current. When you pass this ref to a React element via the ref attribute, React automatically sets the current property to the corresponding DOM node once the component mounts.

Crucially, updating the .current property does not trigger a re-render. This makes useRef perfect for:

  1. DOM access: Storing a reference to an element.
  2. Mutable storage: Holding values (like timers or previous state) that shouldn't cause the UI to flicker when updated.

Worked Example: Managing Focus and Scroll

In our dashboard project, we often need to ensure the user can quickly interact with search inputs. Let’s build a search component that focuses the input automatically and scrolls to a specific results container.

JSX
import { useRef } from CE9178">'react';

function DashboardSearch() {
  // 1. Initialize the ref with null
  const inputRef = useRef(null);
  const scrollContainerRef = useRef(null);

  const handleFocus = () => {
    // 2. Access the DOM node via .current
    inputRef.current.focus();
  };

  const handleScroll = () => {
    // 3. Imperative DOM manipulation
    scrollContainerRef.current.scrollIntoView({ behavior: CE9178">'smooth' });
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Search dashboard..." />
      <button onClick={handleFocus}>Focus Input</button>
      
      <div style={{ height: CE9178">'200vh' }}>
        <button onClick={handleScroll}>Scroll to Bottom</button>
        <div ref={scrollContainerRef}>Target Section</div>
      </div>
    </div>
  );
}

In this example, inputRef and scrollContainerRef are created during the initial render. Because they are persistent containers, they remain the same object throughout the lifecycle of the component. When the component mounts, React assigns the DOM elements to the current property.

Hands-on Exercise

To solidify your understanding, add a "Clear and Focus" button to your current dashboard search bar.

  1. Create a ref for your search input.
  2. Create an onClick handler that clears the input's value by accessing inputRef.current.value = ''.
  3. Immediately call inputRef.current.focus() after clearing the value.
  4. Observe how you are bypassing React’s state-driven update flow to force an imperative change.

Note: In production, you would typically use controlled components for inputs, but understanding this "uncontrolled" approach is vital for integrating non-React libraries.

Common Pitfalls

While useRef is powerful, it’s easy to misuse:

  • Reading/Writing during render: Do not read or write to ref.current during the main body of your component function. Only do it inside useEffect or event handlers. Accessing it during render can cause unpredictable behavior if React's rendering process changes.
  • Assuming the ref is ready: Always check if ref.current is not null before calling methods like .focus() or .scrollIntoView(). If your component conditionally renders the element, the ref might be null.
  • Overusing refs for UI state: If you find yourself using useRef to store information that should affect what the user sees, you probably want useState instead. As we discussed in React useRef and Component Memory: Why Variables Reset on Re-render, refs are for "hidden" data, not data that drives the UI.

Recap

useRef is a specialized tool. Use it when you need to bridge the gap between React's declarative world and the browser's imperative DOM APIs. Remember:

  • It returns an object { current: ... }.
  • Updating it does not trigger a re-render.
  • It is the standard way to handle focus, scroll, and integration with third-party DOM libraries.

By mastering useRef, you gain the control necessary to build truly interactive, complex dashboard widgets that go beyond simple data binding.

Up next: We will dive deeper into persistent mutable values and how to use refs to track previous prop values without stale closures.

Similar Posts