🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
REFERENCEreact

react Documentation

LOADING ENGINE...

useRef

AI & DATA SCIENCE // useref

useRef creates a mutable object that persists for the component's entire lifetime without triggering a re-render when its value changes, commonly used to access a DOM element directly.

Syntax

const myRef = useRef(initialValue);
// access/update via myRef.current

Deep Dive Course

useRef(initialValue) returns an object with a single mutable property, .current, initialized to initialValue — unlike state, updating a ref's .current value doesn't cause the component to re-render, and unlike a plain local variable, a ref's value persists identically across every re-render of the component rather than being reset each time. It has two extremely common uses: holding a direct reference to a DOM element by passing the ref object to a JSX element's ref attribute, and storing any mutable value that needs to persist across renders but genuinely shouldn't trigger a re-render when it changes, like a timer ID or a previous value for comparison.

1Understanding useRef

useRef(initialValue) returns an object with a single mutable property, .current, initialized to initialValue — unlike state, updating a ref's .current value doesn't cause the component to re-render, and unlike a plain local variable, a ref's value persists identically across every re-render of the component rather than being reset each time. It has two extremely common uses: holding a direct reference to a DOM element by passing the ref object to a JSX element's ref attribute, and storing any mutable value that needs to persist across renders but genuinely shouldn't trigger a re-render when it changes, like a timer ID or a previous value for comparison.

💡

Reading or writing ref.current never causes a re-render — if you need the UI to actually update in response to a value changing, that value belongs in state via useState, not in a ref.

editor.html
import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  const focusInput = () => inputRef.current.focus();
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus the input</button>
    </>
  );
}
localhost:3000

2Practical Example

Here is a real-world application of useRef showing how it is used in production React code.

editor.html
import { useRef, useEffect } from 'react';

function RenderCounter() {
  const renderCount = useRef(0);
  useEffect(() => {
    renderCount.current += 1;
    console.log('Rendered', renderCount.current, 'times');
  });
  return <p>Check the console.</p>;
}
localhost:3000

3Best Practices

Follow these guidelines when working with useRef:

1. Use a ref specifically to access a DOM element directly, like calling .focus() on an input, by passing the ref object to that element's ref attribute

2. Store values that need to persist across renders but shouldn't trigger a re-render when they change, like a timer ID or previous-value tracker, in a ref rather than state

3. Avoid reading or writing ref.current during rendering itself, since refs are meant for imperative access outside the render/pure-function flow, typically inside event handlers or effects

⚠️

Tip: Reading or writing ref.current never causes a re-render — if you need the UI to actually update in response to a value changing, that value belongs in state via useState, not in a ref.

editor.html
import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  const focusInput = () => inputRef.current.focus();
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus the input</button>
    </>
  );
}
localhost:3000

4The Previous Value Pattern

A common use of useRef is remembering a prop or state value from the previous render, to compare it against the current one. An effect updates the ref to the latest value after each render commits, so during render itself the ref still holds whatever was current on the render before — a lightweight way to detect that a value just changed.

editor.html
function PriceTag({ price }) {
  const prevPrice = useRef(price);
  useEffect(() => { prevPrice.current = price; });
  const isIncreasing = price > prevPrice.current;
  return <span>{isIncreasing ? '↑' : '↓'} {price}</span>;
}
localhost:3000

5Refs vs. State: A Decision Framework

Reach for useState whenever a value's change should be reflected on screen — React needs to know about it to re-render. Reach for useRef whenever you need to remember something across renders that the UI never directly displays: a DOM node, a timer ID, a render counter, or a previous value for comparison.

editor.html
// Visible in the UI -> useState
const [count, setCount] = useState(0);

// Invisible bookkeeping -> useRef
const timerId = useRef(null);
localhost:3000

Examples

Example 01Basic Usage
import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  const focusInput = () => inputRef.current.focus();
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus the input</button>
    </>
  );
}
Example 02Advanced Example
import { useRef, useEffect } from 'react';

function RenderCounter() {
  const renderCount = useRef(0);
  useEffect(() => {
    renderCount.current += 1;
    console.log('Rendered', renderCount.current, 'times');
  });
  return <p>Check the console.</p>;
}
Example 03Tracking a Previous Prop Value
import { useRef, useEffect } from 'react';

function PriceTag({ price }) {
  const prevPrice = useRef(price);
  useEffect(() => { prevPrice.current = price; });
  const isIncreasing = price > prevPrice.current;
  return <span>{isIncreasing ? '↑' : '↓'} {price}</span>;
}

Best Practices

  • Use a ref specifically to access a DOM element directly, like calling .focus() on an input, by passing the ref object to that element's ref attribute
  • Store values that need to persist across renders but shouldn't trigger a re-render when they change, like a timer ID or previous-value tracker, in a ref rather than state
  • Avoid reading or writing ref.current during rendering itself, since refs are meant for imperative access outside the render/pure-function flow, typically inside event handlers or effects
  • Update a 'previous value' ref from inside a useEffect, not during render, so it reflects the prior render's value rather than the current one
  • Remember that a DOM ref's .current becomes null once that element unmounts — guard any cleanup code that reads it accordingly

Interview Question

Why doesn't updating a ref's .current value cause a component to re-render, while updating state does?

Hint: Think about what specifically triggers React to schedule a re-render — is it any value in the component changing, or something more specific to the state mechanism?

React only schedules a re-render in direct response to a call to a state setter function, like the one returned by useState, or an equivalent state-management mechanism, since that's the specific signal React's rendering system is built to listen for. A ref's .current property is just a plain, regular mutable property on a plain object that useRef happens to return and persist across renders, updating it is functionally no different from mutating any other regular JavaScript object property, and React has no special hook into that property being reassigned, so nothing about updating it ever triggers React's re-render machinery. This is precisely the intended, useful distinction: refs deliberately provide a way to hold and update a persistent value across renders without paying the cost, or triggering the effect, of a re-render, which is exactly what you want for something like tracking a previous value purely for internal comparison, but is exactly the wrong tool if the UI actually needs to visually update in response to that value changing, which requires state instead.

Exercises

MediumPractice using useRef in a real scenario.
View Solution
import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  const focusInput = () => inputRef.current.focus();
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus the input</button>
    </>
  );
}
MediumBuild a component that shows whether a `score` prop increased or decreased since the last render, using useRef to remember the previous value.
View Solution
import { useRef, useEffect } from 'react';

function ScoreTrend({ score }) {
  const prevScore = useRef(score);
  useEffect(() => { prevScore.current = score; });
  const trend = score > prevScore.current ? 'up' : score < prevScore.current ? 'down' : 'same';
  return <span>Score: {score} ({trend})</span>;
}

Frequently Asked Questions

Why doesn't updating a ref's .current value cause a component to re-render, while updating state does?

React only schedules a re-render in direct response to a call to a state setter function, like the one returned by useState, or an equivalent state-management mechanism, since that's the specific signal React's rendering system is built to listen for. A ref's .current property is just a plain, regular mutable property on a plain object that useRef happens to return and persist across renders, updating it is functionally no different from mutating any other regular JavaScript object property, and React has no special hook into that property being reassigned, so nothing about updating it ever triggers React's re-render machinery. This is precisely the intended, useful distinction: refs deliberately provide a way to hold and update a persistent value across renders without paying the cost, or triggering the effect, of a re-render, which is exactly what you want for something like tracking a previous value purely for internal comparison, but is exactly the wrong tool if the UI actually needs to visually update in response to that value changing, which requires state instead.

Why is it unsafe to read a DOM ref's .current value directly during render, instead of inside an effect or event handler?

On a component's very first render, React hasn't created the actual DOM nodes yet — that only happens when React commits the render to the real DOM, which happens after the component function has already returned its JSX. Reading ref.current during that first render call, before commit, would see null (or the ref's initial value) rather than the element, since the element simply doesn't exist yet at that point in time. Effects run after the DOM has been updated, and event handlers only fire once the component is already fully mounted and interactive, so both are safe places to read a DOM ref; the render phase itself is not.

Related Functions

usestateuseeffectusememo