✓React 16.8+ (Hooks), JavaScript ES6, Basic understanding of React components and state management, Node.js 14+ for running examples, Code editor (VS Code recommended)
⚡Quick Answer
React useRef and DOM Manipulation: A core React concept for building modern user interfaces. It helps you structure your components efficiently and handle data flow predictably.
✦ Definition~90s read
What is useRef and DOM Manipulation?
React useRef and DOM Manipulation is a fundamental concept in React development. It refers to the patterns, APIs, and best practices that React provides for building user interfaces. Understanding this concept is essential for writing clean, efficient, and maintainable React code. This tutorial covers everything from basic usage to advanced patterns with real-world examples.
★
React is a JavaScript library for building user interfaces.
Plain-English First
React is a JavaScript library for building user interfaces. This article covers useref dom — a key concept for building modern web applications.
⚙ Browser compatibility
Latest versions — ✓ supported
Chrome
Firefox
Safari
Edge
✓
✓
✓
✓
A comprehensive guide to react useref and dom manipulation with production examples and best practices.
What is useRef? The Mutable Ref Object
useRef is a React hook that returns a mutable ref object whose .current property is initialized to the passed argument. Unlike state, changing .current does not cause a re-render. This makes useRef ideal for storing values that persist across renders without triggering updates. The ref object itself is stable across the component's lifecycle — the same object reference is returned on every render. This stability is key for DOM access, as it allows you to attach a ref to a JSX element and reliably access the underlying DOM node after mounting. useRef is not just for DOM; it can hold any mutable value, like timers, previous state, or instance variables. However, its most common use case is direct DOM manipulation, which we'll explore in depth.
BasicRefExample.jsxJSX
1
2
3
4
5
6
7
8
9
10
11
12
import { useRef, useEffect } from 'react';
export default function InputFocus() {
const inputRef = useRef(null);
useEffect(() => {
// inputRef.current is the DOM node after mount
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" placeholder="Auto-focused" />;
}
Output
On render, the input element receives focus automatically.
The ref object identity is stable across re-renders. React guarantees that the same ref object is returned each time, so you can safely use it in dependency arrays without causing effect re-runs.
📊 Production Insight
In production, never rely on ref.current being available during render — it's undefined until after mount. Always access it inside useEffect or event handlers.
🎯 Key Takeaway
useRef returns a mutable object with a .current property that persists across renders without causing re-renders.
React Useref Dom
Accessing DOM Nodes with the ref Attribute
React provides a built-in ref attribute on host elements (like <div>, <input>, etc.) that accepts a ref object. When the element mounts, React sets ref.current to the corresponding DOM node. When the element unmounts, it sets ref.current back to null. This is the primary way to get a handle on a DOM element for imperative operations like focusing, measuring, or animating. You can also use callback refs for more control, but the object ref is simpler and sufficient for most cases. Important: ref only works on host elements, not on custom components unless they forward refs using forwardRef. We'll cover that later.
Reading ref.current during render (e.g., in the component body) can cause inconsistencies because the DOM may not be updated yet. Always read refs in effects or event handlers.
📊 Production Insight
Measuring DOM elements in useEffect can cause layout thrashing if not batched. Use requestAnimationFrame or ResizeObserver for performance-critical measurements.
🎯 Key Takeaway
Attach a ref object to a host element via the ref attribute to access its DOM node after mount.
Imperative DOM Manipulation: When and How
React is declarative — you describe what the UI should look like, and React handles the DOM updates. However, some operations are inherently imperative: focusing an input, scrolling to a position, playing a video, or integrating with a third-party library that expects a DOM node. useRef gives you an escape hatch for these cases. The rule of thumb: if you can do it declaratively (e.g., using state to control visibility), do it declaratively. Use imperative refs only when there's no declarative equivalent. Common patterns include auto-focus on mount, managing media playback, and triggering animations. Always clean up any side effects (like event listeners) in the useEffect cleanup function to avoid memory leaks.
If you add event listeners to a DOM node via ref, remove them in the useEffect cleanup to prevent memory leaks and duplicate handlers.
📊 Production Insight
In production, avoid calling .focus() on every render — it can steal focus from the user. Conditionally trigger it based on user interaction or route changes.
🎯 Key Takeaway
Use imperative DOM manipulation only when declarative approaches are insufficient, and always clean up side effects.
thecodeforge.io
React Useref Dom
Forwarding Refs to Custom Components
By default, refs are not passed to custom components — they only work on host elements. To expose a DOM node from a child component, you must use React.forwardRef. This higher-order component receives props and ref as arguments, allowing you to attach the ref to a host element inside the child. This is essential for building reusable component libraries where consumers need direct access to the underlying DOM (e.g., for focus management or tooltip positioning). Without forwardRef, you'd have to use callback refs or prop drilling, which is messy. forwardRef is the standard pattern.
When using forwardRef, set a displayName on the component for better debugging: FancyInput.displayName = 'FancyInput';
📊 Production Insight
In production, always type your refs with TypeScript: const inputRef = useRef<HTMLInputElement>(null); and forwardRef<HTMLInputElement, Props>. This catches null reference errors at compile time.
🎯 Key Takeaway
Use React.forwardRef to pass a ref through a custom component to a host element.
Storing Mutable Values Without Re-renders
Beyond DOM access, useRef can store any mutable value that you want to persist across renders without causing re-renders. Common use cases: holding interval IDs, previous state values, or flags that track whether a component is mounted. Because changing .current doesn't trigger a re-render, it's more performant than useState for values that don't affect the UI. However, be careful: mutating refs doesn't notify React, so the UI won't update automatically. If you need the UI to reflect the value, use state instead. A classic pattern is using a ref to store the previous value of a state variable for comparison in useEffect.
Unlike state updates, ref mutations are applied immediately. This can lead to stale closures if you read ref.current inside an async callback that was created earlier.
📊 Production Insight
In production, use a ref to store the latest callback in an effect to avoid stale closures: callbackRef.current = callback; then inside the effect, call callbackRef.current().
🎯 Key Takeaway
useRef can hold any mutable value that persists across renders without causing re-renders.
useRef vs useState: When to Use Which
A common confusion is when to use useRef vs useState. The key difference: useState triggers a re-render when the value changes; useRef does not. If the value affects the UI, use state. If it's a value that only needs to be read or written in effects/event handlers (like timers, DOM nodes, or previous values), use ref. Using state for non-UI values causes unnecessary re-renders and performance issues. Conversely, using ref for UI values means the UI won't update. A good rule: if you need the component to re-render when the value changes, use state; otherwise, use ref.
If you find yourself calling setState with the same value that doesn't change the UI, consider using a ref instead.
📊 Production Insight
In production, using ref for interval IDs prevents memory leaks from orphaned intervals when the component unmounts without stopping the timer.
🎯 Key Takeaway
Use state for values that drive the UI; use ref for values that don't affect rendering.
Callback Refs: Fine-Grained Control
React also supports callback refs — a function instead of a ref object. React calls the function with the DOM node when the element mounts, and with null when it unmounts. Callback refs give you more control, especially when you need to run logic on every ref change (e.g., when the ref is attached to a dynamic list of items). They also allow you to clean up previous refs. However, they can cause more re-renders if not memoized. Use useCallback to stabilize the callback ref and avoid unnecessary invocations. Callback refs are useful for measuring dynamic content or integrating with libraries that need a node reference.
Always wrap callback refs in useCallback to prevent them from being called on every render, which can cause performance issues.
📊 Production Insight
In production, avoid setState inside a callback ref if the list is large — it can cause performance bottlenecks. Batch updates or use a ref to store measurements.
🎯 Key Takeaway
Callback refs provide fine-grained control over when refs are set and cleaned up, useful for dynamic lists.
Common Pitfalls and Anti-Patterns
Even experienced developers make mistakes with useRef. Common pitfalls: (1) Reading ref.current during render — it may be null or stale. Always access in effects or handlers. (2) Using refs to store state that should trigger re-renders — leads to stale UI. (3) Forgetting to clean up refs on unmount, causing memory leaks (e.g., not clearing intervals). (4) Overusing refs for DOM manipulation when a declarative approach exists — makes code harder to maintain. (5) Mutating refs inside render — can cause inconsistencies. Stick to the patterns: refs for DOM access and mutable values, state for UI. Test your ref logic with strict mode to catch double-mount issues.
PitfallExample.jsxJSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// BAD: reading ref during render
function BadComponent() {
const ref = useRef(null);
// ref.current is null on first render
const width = ref.current?.offsetWidth; // undefined
return <div ref={ref}>Width: {width}</div>;
}
// GOOD: read in effect
function GoodComponent() {
const ref = useRef(null);
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(ref.current?.offsetWidth ?? 0);
}, []);
return <div ref={ref}>Width: {width}</div>;
}
Output
BadComponent shows 'Width: undefined'; GoodComponent shows the actual width after mount.
If you read ref.current inside a closure (e.g., setTimeout), it captures the value at closure creation time. Use a ref to store the latest value and read it inside the closure.
📊 Production Insight
In production, enable React Strict Mode in development to detect side-effect issues with refs, such as missing cleanup or double-mount problems.
🎯 Key Takeaway
Avoid reading ref.current during render, clean up side effects, and prefer declarative approaches when possible.
createRef vs useRef: Class Component Legacy
In React class components, refs are created using React.createRef(). This method returns a ref object with a current property, similar to useRef in functional components. However, createRef is only available in class components and is not a hook. The key difference is that createRef creates a new ref object on every render, while useRef persists the same ref object across renders. In functional components, useRef is the standard approach because it leverages hooks and avoids unnecessary object creation. createRef exists primarily for legacy class component support. If you're migrating from class to functional components, replace React.createRef() with useRef(null). Note that createRef cannot be used inside functional components; doing so will cause a warning and break ref persistence. For new code, always prefer useRef.
When migrating from class to functional components, replace React.createRef() with useRef(null) to maintain ref behavior and avoid unnecessary re-creation.
🎯 Key Takeaway
Use useRef in functional components; createRef is a legacy API for class components that creates a new ref each render.
flushSync: Coordinating Ref Access with DOM Updates
React batches state updates and defers DOM commits for performance. This means after calling setState, the DOM may not be updated immediately, so reading a ref's current property might return stale data. flushSync from react-dom forces React to flush any pending updates and update the DOM synchronously before proceeding. This is useful when you need to measure DOM properties (e.g., scroll position, element dimensions) immediately after a state change. However, use flushSync sparingly as it can degrade performance by bypassing batching. Example: after adding an item to a list, you want to scroll to the new item. Without flushSync, scrollIntoView might run before the new element is rendered. Wrap the state update and ref access in flushSync to ensure the DOM is up-to-date.
FlushSyncExample.jsJSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
importReact, { useRef, useState } from 'react';
import { flushSync } from 'react-dom';
function ScrollList() {
const [items, setItems] = useState(['Item 1']);
const listEndRef = useRef(null);
const addItem = () => {
flushSync(() => {
setItems(prev => [...prev, `Item ${prev.length + 1}`]);
});
// DOM is now updated, ref points to new element
listEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
<li ref={listEndRef} />
</ul>
<button onClick={addItem}>AddItem</button>
</div>
);
}
In production, prefer useEffect for side effects that depend on DOM state, as it runs after the DOM is committed. Reserve flushSync for imperative scenarios like scroll management or measurement that cannot be deferred.
🎯 Key Takeaway
Use flushSync to synchronously flush DOM updates when you need to read or manipulate the DOM immediately after a state change.
useRef vs useState for ValuesChoosing between mutable refs and state for data storageuseRefuseStateRe-renders on updateNo re-render when .current changesTriggers component re-renderPersistence across rendersPersists value without causing re-renderPersists value but triggers re-render onUse case for DOMDirect DOM node access and manipulationNot suitable for DOM referencesMutable vs ImmutableMutable object (change .current directlyImmutable state (use setter function)THECODEFORGE.IO
thecodeforge.io
React Useref Dom
ResizeObserver + useRef for Responsive Layout
ResizeObserver is a browser API that watches for changes to an element's size. Combined with useRef, you can create responsive components that react to container size changes without relying on window resize events. This is ideal for charts, grids, or any layout that needs to adapt to its container. Use useRef to hold a reference to the observed element and store the observer instance. In a useEffect, create the observer, attach it to the ref's current node, and update state with new dimensions. Clean up by disconnecting the observer on unmount. Example: a responsive card that changes its layout based on width. Note that ResizeObserver is supported in modern browsers; for older ones, consider a polyfill.
For production, consider using a library like @react-hook/resize-observer for a more robust hook-based API, and always disconnect observers in cleanup to prevent memory leaks.
🎯 Key Takeaway
Use ResizeObserver with useRef to create components that dynamically respond to container size changes, enabling responsive layouts without window resize events.
⚙ Quick Reference
11 commands from this guide
File
Command / Code
Purpose
BasicRefExample.jsx
export default function InputFocus() {
What is useRef? The Mutable Ref Object
MeasureElement.jsx
export default function MeasureExample() {
Accessing DOM Nodes with the ref Attribute
VideoPlayer.jsx
export default function VideoPlayer({ src }) {
Imperative DOM Manipulation
FancyInput.jsx
const FancyInput = forwardRef((props, ref) => {
Forwarding Refs to Custom Components
PreviousValue.jsx
export default function Counter() {
Storing Mutable Values Without Re-renders
TimerExample.jsx
export default function Timer() {
useRef vs useState
DynamicListRefs.jsx
export default function List() {
Callback Refs
PitfallExample.jsx
function BadComponent() {
Common Pitfalls and Anti-Patterns
ClassComponent.js
class MyComponent extends Component {
createRef vs useRef
FlushSyncExample.js
function ScrollList() {
flushSync
ResizeObserverExample.js
function ResponsiveCard() {
ResizeObserver + useRef for Responsive Layout
Key takeaways
1
Mutable Ref Object
useRef returns a stable object with a .current property that persists across renders without causing re-renders.
2
DOM Access
Attach refs to host elements to imperatively focus, measure, or manipulate the DOM, but prefer declarative approaches when possible.
3
forwardRef
Use React.forwardRef to expose a child's DOM node to a parent, essential for reusable component libraries.
4
Non-UI State
Store mutable values like timers or previous state in refs to avoid unnecessary re-renders and improve performance.
Common mistakes to avoid
3 patterns
×
Not understanding React component re-rendering
Fix
Use React.memo, useMemo, and useCallback strategically to prevent unnecessary re-renders.
×
Ignoring the rules of hooks
Fix
Always call hooks at the top level, not inside conditions, loops, or callbacks.
×
Mutating state directly instead of using setState
Fix
Always use the state setter function and treat state as immutable.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01JUNIOR
What is the Virtual DOM and how does React use it?
Q02JUNIOR
Explain the difference between state and props.
Q03JUNIOR
What is the purpose of the useEffect hook?
Q04JUNIOR
How does React handle keys in lists?
Q01 of 04JUNIOR
What is the Virtual DOM and how does React use it?
ANSWER
The Virtual DOM is a lightweight JavaScript representation of the real DOM. React diffs the Virtual DOM with the previous version and applies only the changed parts to the real DOM.
Q02 of 04JUNIOR
Explain the difference between state and props.
ANSWER
Props are read-only data passed from parent to child. State is mutable data managed within a component. Changes to state trigger re-renders.
Q03 of 04JUNIOR
What is the purpose of the useEffect hook?
ANSWER
useEffect handles side effects in functional components: data fetching, subscriptions, DOM manipulation, and timers. It replaces lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount.
Q04 of 04JUNIOR
How does React handle keys in lists?
ANSWER
Keys help React identify which items have changed, been added, or removed. Use stable, unique IDs (not array indices) as keys for optimal performance.
01
What is the Virtual DOM and how does React use it?
JUNIOR
02
Explain the difference between state and props.
JUNIOR
03
What is the purpose of the useEffect hook?
JUNIOR
04
How does React handle keys in lists?
JUNIOR
FAQ · 6 QUESTIONS
Frequently Asked Questions
01
Can I use useRef to store a value and have the component re-render when it changes?
No, useRef does not trigger re-renders. If you need the UI to update when the value changes, use useState instead. useRef is for values that persist across renders without affecting the UI.
Was this helpful?
02
How do I pass a ref to a child component?
Use React.forwardRef in the child component to forward the ref to a host element. The parent creates a ref with useRef and passes it via the ref attribute to the child.
Was this helpful?
03
What is the difference between useRef and createRef?
useRef creates a ref that persists across renders in functional components. createRef creates a new ref on every render, which is only useful in class components. In functional components, always use useRef.
Was this helpful?
04
Can I use useRef to store a previous state value?
Yes, you can store the previous value of a state variable by updating a ref in a useEffect that runs after every render. Then read the ref's current value to get the previous state.
Was this helpful?
05
Is it safe to mutate ref.current directly?
Yes, mutating ref.current is safe and does not cause re-renders. However, avoid mutating it during render to prevent inconsistencies. Mutate it inside effects or event handlers.
Was this helpful?
06
How do I measure a DOM element's size after it resizes?
Use a combination of useRef to get the element and a ResizeObserver inside a useEffect. Store the observer in a ref to clean it up on unmount. Update state with the new measurements.