Master How to Use React useRef for DOM Manipulation

how to use React useRef for DOM manipulation
Frontend

React has come a long way in how it helps developers manage state, side effects, and references to DOM elements. One such powerful tool in the React Hooks is the useRef hook.

In this guide, you’ll learn everything you need to know about React useRef. I’ll cover what it is, when to use it, and how it can help with direct DOM manipulation and performance optimization. Whether you’re working with react useRef TypeScript or just exploring how useRef in React works, you’re in the right place.

 

Why DOM Access and Performance Matter

Modern web apps are interactive. Sometimes, you need to access the DOM directly to focus an input, scroll an element, or integrate with third-party libraries. Also, optimizing performance is crucial. Avoiding unnecessary re-renders helps keep apps smooth.

This guide will walk you through how to use React useRef effectively, including real-world scenarios and code examples.

Understanding useRef: The Basics

What is useRef?

useRef is a React hook that returns a mutable ref object. This object has a current property that holds any value you assign. Unlike useState, updating a ref doesn’t trigger a re-render.

Core Usage:

  • Accessing DOM elements directly.
  • Storing mutable values that persist across renders.

Example:

const myRef = useRef(null); // Initializes with null

How useRef Differs from useState and useEffect

  • useState stores values and triggers re-renders when they change.
  • useEffect runs side effects after render.
  • useRef stores values or DOM references and does not cause re-renders.

Anatomy of a useRef Object

The object returned by useRef() has just one property:

myRef.current // Holds the value or DOM element

When and Why to Use useRef

Use useRef when you need:

  • DOM access (focusing, scrolling, measuring)
  • Mutable variables (like interval IDs)
  • Persisted values that don’t trigger re-renders

This makes useRef perfect for tasks like:

  • Focusing an input after mount
  • Storing previous props/state
  • Avoiding stale closures

Setting Up useRef in Your React App

Before using useRef, make sure your component is functional. Hooks don’t work in class components. useRef is built into React. It’s lightweight and won’t slow your app unless you misuse it.

1. Importing useRef from React

Before using useRef, you need to import it from the react package.

import { useRef } from ‘react’;  

  • React provides useRef as a built-in hook.
  • You must import it to use it in functional components.

2. Initializing a Ref

After importing, declare a ref using useRef().

  • Syntax:

const myRef = useRef(initialValue);

  • initialValue (optional): The starting value of myRef.current.

  • If you’re storing a DOM reference, initialize with null.

Example:

const inputRef = useRef(null); // For DOM elements
const counterRef = useRef(0); // For mutable values

  • The ref object (inputRef) has a current property.

  • Changing current does not trigger a re-render (unlike useState).

3. Attaching Refs to DOM Elements

To interact with a DOM element (like an <input> or <div>), pass the ref using the ref attribute in JSX.

  • Example: Focusing an Input

function TextInput() {

  const inputRef = useRef(null); // Initialize ref

 

  const focusInput = () => {

    inputRef.current.focus(); // Access DOM element

  };

 

  return (

    <div>

      <input ref={inputRef} /> {/* Attach ref */}

      <button onClick={focusInput}>Focus Input</button>

    </div>

  );

}

How It Works:

  • inputRef is created with useRef(null).
  • The <input> gets the ref={inputRef} attribute.
  • When the button is clicked, inputRef.current accesses the DOM input and focuses it.

4. Accessing the Ref’s Current Value

Once a ref is attached to a DOM element, you can access it via ref.current.

  • Example: Logging Element Properties

function MeasureBox() {

  const boxRef = useRef(null);

 

  useEffect(() => {

    if (boxRef.current) {

      const width = boxRef.current.clientWidth;

      console.log(‘Box width:’, width);

    }

  }, []);

 

  return <div ref={boxRef}>Measure Me</div>;

}

  • Always check if (ref.current) before accessing DOM properties (avoids errors).
  • Refs are null on first render if the element isn’t mounted yet.

5. Using useRef with TypeScript (React useRef TypeScript)

If you’re using TypeScript, you can strictly type refs for better safety.

  • Syntax:

const ref = useRef<ElementType>(null);

  • Example: Typing an Input Ref

import { useRef } from ‘react’;

 

function TypedInput() {

  const inputRef = useRef<HTMLInputElement>(null);

 

  const focusInput = () => {

    inputRef.current?.focus(); // Optional chaining (safe access)

  };

 

  return (

    <div>

      <input ref={inputRef} />

      <button onClick={focusInput}>Focus</button>

    </div>

  );

}

Why Type Refs?

  • Prevents incorrect DOM methods (like trying to focus() a div).

  • Improves autocomplete in IDEs.

How to Use React useRef for DOM Manipulation

One of the most common use cases for useRef is interacting directly with DOM elements. Unlike useState, which triggers re-renders, useRef allows you to access and modify DOM nodes efficiently.

In this section, I’ll explore practical examples of how to use React useRef for DOM manipulation, including:

  • Focusing an input field
  • Scrolling to an element
  • Measuring element dimensions
  • Controlling media playback

 

1. Focusing an Input Field Programmatically

A classic use case for useRef is programmatically focusing an input field when a button is clicked.

Step-by-Step Implementation

  • Import useRef and declare the ref:

import { useRef } from ‘react’;

 

function FocusInput() {

  const inputRef = useRef(null); // Initialize with null

  • Attach the ref to an input element:

return (

  <div>

    <input ref={inputRef} />

    <button onClick={() => inputRef.current.focus()}>

      Focus Input

    </button>

  </div>

);

Access the DOM node with inputRef.current:

  • When the button is clicked, inputRef.current.focus() sets focus on the input.

Why This Works

  • useRef holds a reference to the DOM node.
  • Unlike useState, modifying inputRef.current doesn’t cause a re-render.

 

2. Scrolling to an Element Smoothly

Another common scenario is scrolling to a specific section when a button is clicked.

Step-by-Step Implementation

  • Create a ref for the target element:

function ScrollToSection() {

  const sectionRef = useRef(null);

  • Attach the ref to a <div>:

return (

  <div>

    <button onClick={() => sectionRef.current.scrollIntoView({ behavior: ‘smooth’ })}>

      Scroll to Section

    </button>

    <div ref={sectionRef} style={{ height: ‘500px’, background: ‘lightblue’ }}>

      Target Section

    </div>

  </div>

);

Trigger scroll with scrollIntoView:

    • behavior: 'smooth' enables smooth scrolling.

When to Use This

  • Navigation menus with anchor links.

  • Scrollable lists where you want to jump to a specific item.

 

3. Measuring Element Size and Position

Sometimes, you need to get an element’s dimensions (width, height) or position (X, Y coordinates).

Step-by-Step Implementation

  • Create a ref for the element:

function MeasureBox() {

  const boxRef = useRef(null);

  • Use getBoundingClientRect() to measure the element:

useEffect(() => {

  if (boxRef.current) {

    const { width, height, top, left } = boxRef.current.getBoundingClientRect();

    console.log(width, height, top, left);

  }

}, []);

  • Attach the ref to a <div>:

return (

  <div ref={boxRef} style={{ width: ‘200px’, height: ‘100px’, border: ‘1px solid black’ }}>

    Measure Me

  </div>

);

Practical Use Cases

  • Dynamic resizing of components.

  • Detecting if an element is visible in the viewport.

 

4. Controlling Video or Audio Playback

useRef is also useful for controlling media elements (video, audio) without re-rendering.

Step-by-Step Implementation

  • Create a ref for the <video> element:

function VideoPlayer() {

  const videoRef = useRef(null);

  • Add play/pause buttons:

return (

  <div>

    <video ref={videoRef} src=”example.mp4″ controls />

    <button onClick={() => videoRef.current.play()}>Play</button>

    <button onClick={() => videoRef.current.pause()}>Pause</button>

  </div>

);

  • No unnecessary re-renders when play/pause is toggled.

  • Direct access to the DOM element’s methods.

useRef vs. useState vs. createRef

To get React’s useRef, compare it to useState and createRef.

Feature useRef (Functional) useState createRef (Class)
Persists across renders
Yes
Yes
Yes
Triggers re-render
No
Yes
No
Use in functional components
Yes
Yes
No (class only)
DOM access
Yes
No
Yes
Typical use case
DOM access, mutable data
UI state
DOM access in class components

When to Use Each

  • useRef: Use it to access DOM elements, timers, or values that shouldn’t trigger re-renders. It’s best for functional components.
  • useState: Use it for state that updates the UI, like form inputs or counters.
  • createRef: Use it in class components for DOM access. You won’t need it much in modern React.

 

useRef is the best choice for functional components. It’s simple, efficient, and flexible.

Advanced Patterns and Real-World Applications

React useRef isn’t just for DOM access, it’s a Swiss Army knife for advanced patterns. Let’s explore some real-world applications.

Tracking Previous State Values

You can use useRef to store previous state or props:

import { useState, useRef, useEffect } from ‘react’;

 

function PreviousValue() {

  const [count, setCount] = useState(0);

  const prevCountRef = useRef();

 

  useEffect(() => {

    prevCountRef.current = count; // Store previous count

  }, [count]);

 

  const handleIncrement = () => setCount(count + 1);

 

  return (

    <div>

      <p>Current: {count}</p>

      <p>Previous: {prevCountRef.current}</p>

      <button onClick={handleIncrement}>Increment</button>

    </div>

  );

}

  • Here, prevCountRef.current keeps the old count value. It helps you compare changes without re-rendering.

Managing timers and animation frames

Use useRef to handle timers or animation frames. Store the timer ID in a ref so you can clean it up easily.

import { useRef, useEffect } from ‘react’;

 

function Timer() {

  const timerRef = useRef(null);

 

  useEffect(() => {

    timerRef.current = setInterval(() => {

      console.log(‘Tick’);

    }, 1000);

 

    return () => clearInterval(timerRef.current); // Cleanup

  }, []);

 

  return <div>Timer running…</div>;

}

  • This stops memory leaks by clearing the interval when the component unmounts.

Working with third-party libraries

Some libraries (like Chart.js or GSAP) need to use DOM elements. React’s useRef helps with this.

import { useRef, useEffect } from ‘react’;

import Chart from ‘chart.js/auto’;

 

function ChartComponent() {

  const canvasRef = useRef(null);

 

  useEffect(() => {

    const ctx = canvasRef.current.getContext(‘2d’);

    new Chart(ctx, {

      type: ‘bar’,

      data: {

        labels: [‘Red’, ‘Blue’, ‘Yellow’],

        datasets: [{ data: [12, 19, 3] }],

      },

    });

  }, []);

 

  return <canvas ref={canvasRef} />;

}

  • Here, canvasRef.current gives Chart.js the <canvas> element. React’s useRef keeps things smooth without re-renders.

TypeScript with useRef

If you use TypeScript, you can add type safety:

import { useRef, useEffect } from ‘react’;

 

function TypedComponent() {

  const inputRef = useRef<HTMLInputElement>(null);

 

  useEffect(() => {

    if (inputRef.current) {

      inputRef.current.focus();

    }

  }, []);

 

  return <input ref={inputRef} type=”text” />;

}

Using HTMLInputElement tells TypeScript what inputRef.current is. This stops errors.

FAQs in React useRef

can refs be used to force re-renders in react

No, refs do not trigger re-renders. Unlike useState, updating a ref's .current value does not cause the component to re-render. If you need to force a re-render after updating a ref, you should combine it with a state update using useState or useReducer. Refs are best used for storing mutable values or accessing DOM nodes without impacting the render cycle.

how do refs interact with react’s reconciliation process using react

Refs are bypassed during the reconciliation process. React does not track the value of ref.current and does not re-run the render when it changes. This makes them ideal for interacting with the DOM or storing values that shouldn't influence the UI rendering. React only updates the ref attribute on a DOM element when the component mounts or updates with a new ref.

are refs safe for concurrent rendering in react

Yes, refs are considered safe for concurrent rendering in React (like with React 18's concurrent mode). React ensures that refs are updated consistently after the commit phase. However, you should avoid reading from or writing to refs during render, as the values may not be stable or final in concurrent rendering. Always access .current inside useEffect, event handlers, or callbacks to ensure correctness.

In summary useRef()

The useRef hook in React is a practical and performance-conscious tool for managing mutable values and DOM references without triggering re-renders. It’s essential when working with tasks like focusing an input, measuring element dimensions, or setting up timers and third-party libraries.

Key takeaways:

  • useRef allows direct DOM interaction and value persistence.
  • It avoids unnecessary re-renders, helping maintain app performance.
  • It’s a go-to tool when you need mutable storage or DOM access in functional components.

 

With useRef, you get better control over side effects, UI behavior, and performance. Try the code examples in this guide to see how useRef can simplify your work.

Leave a Reply

Your email address will not be published. Required fields are marked *