Skip to main content

Overview

The useRef hook returns a mutable ref object whose .current property is initialized to the passed argument. The returned object persists for the full lifetime of the component.

Signature

const ref = useRef(initialValue)

Parameters

initialValue
any
The initial value for the ref.current property. This value is only used on the first render.

Returns

ref
object
A mutable ref object with a single property:
{
  current: initialValue
}
The current property can be mutated directly and persists across re-renders.

Usage Examples

Storing DOM References

import { useRef, useEffect } from '@glyphui/runtime';

function TextInput() {
  const inputRef = useRef(null);
  
  useEffect(() => {
    // Focus the input on mount
    inputRef.current?.focus();
  }, []);
  
  return <input ref={inputRef} type="text" />;
}

Storing Mutable Values

Use refs to store values that don’t trigger re-renders when changed:
function Timer() {
  const [count, setCount] = useState(0);
  const intervalRef = useRef(null);
  
  const startTimer = () => {
    intervalRef.current = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);
  };
  
  const stopTimer = () => {
    clearInterval(intervalRef.current);
  };
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={startTimer}>Start</button>
      <button onClick={stopTimer}>Stop</button>
    </div>
  );
}

Tracking Previous Values

import { useState, useRef, useEffect } from '@glyphui/runtime';

function Counter() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();
  
  useEffect(() => {
    prevCountRef.current = count;
  });
  
  const prevCount = prevCountRef.current;
  
  return (
    <div>
      <p>Current: {count}</p>
      <p>Previous: {prevCount}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Avoiding Re-renders

Unlike state, updating a ref does not trigger a re-render:
function ClickTracker() {
  const clickCountRef = useRef(0);
  const [renderCount, setRenderCount] = useState(0);
  
  const handleClick = () => {
    // This does NOT trigger a re-render
    clickCountRef.current += 1;
    console.log('Clicks:', clickCountRef.current);
  };
  
  const forceRender = () => {
    // This DOES trigger a re-render
    setRenderCount(r => r + 1);
  };
  
  return (
    <div>
      <p>Clicks: {clickCountRef.current}</p>
      <p>Renders: {renderCount}</p>
      <button onClick={handleClick}>Click (no render)</button>
      <button onClick={forceRender}>Force Render</button>
    </div>
  );
}

Storing Callbacks

function Component({ onEvent }) {
  const callbackRef = useRef(onEvent);
  
  // Update ref when callback changes
  useEffect(() => {
    callbackRef.current = onEvent;
  }, [onEvent]);
  
  useEffect(() => {
    const handler = (data) => {
      // Always calls the latest callback
      callbackRef.current(data);
    };
    
    eventEmitter.on('event', handler);
    return () => eventEmitter.off('event', handler);
  }, []); // No dependency on onEvent
  
  return <div>Component</div>;
}

Key Differences from State

FeatureuseRefuseState
Triggers re-renderNoYes
MutableYes (via .current)No (immutable)
PersistenceAcross rendersAcross renders
Use caseDOM refs, mutable valuesUI state

Rules and Behavior

  1. Persistent across renders - The same ref object is returned on every render
  2. Mutable - You can mutate ref.current directly without causing re-renders
  3. Initialization - The initial value is only used on the first render
  4. No re-render - Changing ref.current does not trigger a re-render
  5. Must be called at top level - Don’t call useRef inside loops, conditions, or nested functions
  6. Only in functional components - Cannot be used in class components

Common Use Cases

  • Accessing DOM elements directly
  • Storing interval or timeout IDs
  • Keeping track of previous state values
  • Storing mutable values that shouldn’t trigger re-renders
  • Caching callback functions
  • Storing instance variables in functional components

Implementation Details

  • Returns the same object reference ({ current: value }) across all renders
  • Stored in the component’s hooks data structure
  • Does not trigger any re-render mechanism when .current is modified
  • The ref object itself never changes, only its .current property can be mutated

Build docs developers (and LLMs) love