Let’s be honest — every React developer, at some point, hits that weird wall. You’ve got your states. You’ve got your props. But then you need to focus an input field, control a video playback, or maybe access a third-party animation library that insists on direct DOM access. And React? It doesn’t exactly hand you the DOM on a silver platter. That’s where Refs in React and useRef in React quietly step in — the unsung heroes that bridge React’s declarative world with the imperative control developers sometimes need.
According to recent data from npm trends (Sept 2025), the useRef hook appears in nearly 43% of active React repositories, making it one of the top five most used hooks after useState, useEffect, and useContext. That’s not just trivia — it’s a reflection of how vital refs have become in building smooth, production-grade interfaces.
Think about this:
- Every time an input field automatically gains focus on login, that’s likely a ref.
- Every “scroll to section” interaction on a portfolio site? Refs.
- Even fancy animations or chart integrations in dashboards — yep, refs again.
For developers aiming to move beyond beginner React tutorials and into real-world, career-level React development, understanding how and when to use refs is non-negotiable.
Because when you know how to balance React’s declarative style with the right amount of imperative control, you start building experiences that feel more alive, more intuitive — and frankly, more professional.
So, before diving deep into hooks and code, let’s lock down the basics — what refs are, what referencing means in React, and why developers reach for them in the first place.
⚡ Key Highlights
✅ Learn what Refs in React are and how they work under the hood
✅ Understand the concept of referencing in React and its real-world use
✅ Discover why useRef has become an essential tool for front-end developers
✅ See how refs bridge declarative and imperative programming in React
✅ Includes real-world examples, developer insights, and performance tips
🧠 What Are Refs in React?
At its core, Refs in React (short for references) are a way to directly access or interact with a DOM node or a React element — without triggering re-renders.
Normally, React keeps a strict boundary between your components and the actual DOM. This helps maintain predictable UI updates through its virtual DOM system. But sometimes you need to bypass that layer — maybe to set focus, measure an element’s size, or control a third-party library that expects a raw DOM element.
That’s where refs come in.
A ref is like a pointer — a special object that lets you “reference” a specific element in your UI. React attaches this reference to the element and stores it under .current, giving you direct access to the underlying node.
For example:
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
This tiny piece of code is doing something powerful: it’s telling React, “Hey, once this input is rendered, grab it and focus it.”
No extra re-render. No state juggling. Just direct, surgical access.
💡 What Is Referencing in React and Why Use It?
Referencing in React simply means linking a JavaScript variable to a DOM element or component instance. This lets you read, modify, or control that element programmatically.
Why developers use it:
- To manage focus and selection: Think login forms or input wizards.
- To trigger animations: Especially when working with libraries like GSAP or Framer Motion.
- To integrate with non-React tools: Refs make React play nicely with the wider JavaScript ecosystem.
- To hold mutable data: Like timers, IDs, or previous values — without re-rendering the component.
In short, referencing gives React developers a safe, predictable way to step outside the React bubble when necessary. It’s not about breaking the rules; it’s about knowing when and how to bend them for better UX and performance.
⚙️ What Is useRef in React and Why It Matters
The useRef hook in React is one of those quiet superpowers that often hides in plain sight. It doesn’t make your component re-render, it doesn’t show up in your JSX, but it can single-handedly fix UI glitches that state can’t handle.
In simple terms, useRef is a React Hook that gives you a persistent “container” whose .current property survives across renders.
When you assign that container to an element’s ref attribute, React automatically links it to the DOM node.
But here’s the magic: changing ref.current doesn’t cause a re-render.
Why does this matter? Because it lets you store mutable values—like DOM nodes, timers, or even previous state values—without triggering React’s rendering engine.
Take a simple login form:
When you submit it, you probably want the first invalid field to get focused automatically. Using state for that would cause unnecessary updates. Using useRef? It’s instant, clean, and doesn’t re-render the whole form.
This is why seasoned developers often call useRef “the state that doesn’t trigger renders.”
It’s perfect for performance-sensitive scenarios, especially in large UIs or dashboards where you want precision control over DOM behavior.

🧠 Understanding the Concept of React Refs
Before diving deeper into useRef, let’s zoom out and talk about the bigger idea — React Refs.
React’s philosophy is all about declarative programming. You describe what the UI should look like based on the current state, and React figures out how to make it happen.
Refs are the exception.
They let you momentarily step into the imperative world — to say,
“Hey React, I know you’ve got this, but I need to manually handle this one thing.”
This is common when dealing with:
- DOM measurements (like finding element height or scroll position)
- Animations (where you control timeline objects)
- 3rd-party APIs (that need real DOM nodes)
Internally, React creates an object that looks like this:
{ current: null }
After the component renders, React assigns the actual DOM element to current.
It’s as if React says: “Here’s your handle to that element. Don’t abuse it.” 😄
🧩 useRef in React: Syntax and Basic Example
Here’s the syntax — simple but elegant:
import { useRef, useEffect } from "react";
function InputFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // focuses the input element
}, []);
return <input ref={inputRef} placeholder="Type something..." />;
}
Let’s unpack that:
useRef(null)creates a reference object{ current: null }.- The
refprop on the<input>tells React, “Hey, keep track of this element.” - After the first render, React updates
inputRef.currentto point to that<input>element. - The
useEffecthook runs, focusing the input.
This tiny example is the simplest, most practical use case of useRef in React—auto-focusing an input without causing re-renders.
🏗️ createRef
Before hooks arrived in React 16.8 (2019), there was no useRef.
Developers used createRef() instead — still valid today, but primarily used inside class components.
Example:
import React, { createRef, Component } from "react";
class FormInput extends Component {
constructor(props) {
super(props);
this.inputRef = createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return <input ref={this.inputRef} />;
}
}
createRef always gives you a new ref object every time it’s called — which works fine in classes but can be inefficient in functional components since they re-run on every render.

⚡ useRef
useRef fixes that issue.
Unlike createRef, useRef preserves the same reference object between renders.
It’s like having a stable memory cell that React never resets.
That means you can store anything in it — a DOM node, a counter, or even a previous value:
function Counter() {
const count = useRef(0);
const handleClick = () => {
count.current++;
console.log(`Clicked ${count.current} times`);
};
return <button onClick={handleClick}>Click me</button>;
}
Notice how updating count.current doesn’t cause the component to re-render. That’s the key difference — refs are mutable without being reactive.
This makes them perfect for storing values you don’t want to trigger UI updates (like timers or cached data).

🔗 forwardRef
Now, here’s where things get interesting.
Sometimes you need to pass a ref from a parent component to a child component — for example, if a parent wants to control the child’s input field.
By default, refs don’t automatically pass down through props. That’s where forwardRef steps in.
Example:
import React, { useRef, forwardRef } from "react";
const CustomInput = forwardRef((props, ref) => (
<input ref={ref} {...props} />
));
function Parent() {
const inputRef = useRef(null);
return (
<>
<CustomInput ref={inputRef} placeholder="Enter name" />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
</>
);
}
Here’s what’s happening:
forwardRefallows the child (CustomInput) to receive the parent’s ref.- The parent can now directly control the input inside the child.
It’s a crucial pattern in component libraries (like Material UI or Chakra UI), where parents often need programmatic access to internal inputs, modals, or dropdowns.

⚖️ createRef vs useRef: Key Differences You Should Know
| Feature | createRef() |
useRef() |
|---|---|---|
| Component Type | Class Components | Functional Components |
| Persistence | New ref on every render | Same ref across renders |
| Re-renders | Doesn’t trigger re-renders | Doesn’t trigger re-renders |
| Use Case | Static DOM access inside lifecycle methods | Persistent mutable values & DOM refs |
| Performance | Can recreate frequently | More stable & performant |
Key takeaway:
Use createRef in class-based components (legacy code).
Use useRef in modern functional components.
⚡ Developer Insight:
In large-scale React apps, devs often use useRef not just for DOM access, but for caching objects or storing previous props.
This reduces re-renders and can improve performance by 10–15% in heavy UI workflows — especially in dashboards or visual-heavy apps.
💻 Real-World useRef Hook Examples in React
Knowing the syntax is one thing — but seeing useRef in action inside real-world apps is where it truly clicks. Let’s look at a few situations that professional developers encounter every day.

🧭 1. Managing Focus Dynamically
function SearchBox() {
const inputRef = useRef(null);
const handleSearch = () => {
alert(`Searching for: ${inputRef.current.value}`);
inputRef.current.focus(); // focus back after search
};
return (
<>
<input ref={inputRef} placeholder="Search..." />
<button onClick={handleSearch}>Search</button>
</>
);
}
Why it matters:
In UI/UX-heavy apps like admin dashboards, forms, or AI tools, managing focus dynamically makes the interface feel fast and polished. No state re-renders, no flicker — just direct control.
⏱️ 2. Storing Timers or Intervals
function Stopwatch() {
const timerRef = useRef(null);
const [seconds, setSeconds] = useState(0);
const start = () => {
if (!timerRef.current) {
timerRef.current = setInterval(() => setSeconds(s => s + 1), 1000);
}
};
const stop = () => {
clearInterval(timerRef.current);
timerRef.current = null;
};
return (
<div>
<p>{seconds}s</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
Why useRef here:
If you used state to store timerId, it would re-render the component every time — completely unnecessary. useRef gives you a safe, persistent storage for mutable values like timers.
🎞️ 3. Controlling Media Elements
function VideoPlayer() {
const videoRef = useRef(null);
const handlePlayPause = () => {
const video = videoRef.current;
video.paused ? video.play() : video.pause();
};
return (
<div>
<video ref={videoRef} width="400" src="sample.mp4" />
<button onClick={handlePlayPause}>Play / Pause</button>
</div>
);
}
Where it shines:
In React video/audio apps (like YouTube clones, portfolio demos, or interactive dashboards), useRef allows instant playback control — no state updates, no lag.
🧩 4. Tracking Previous Values
function PreviousValueDemo({ value }) {
const prevValue = useRef();
useEffect(() => {
prevValue.current = value;
});
return (
<p>
Current: {value} | Previous: {prevValue.current}
</p>
);
}
This trick is widely used in custom hooks and performance debugging — tracking previous props or state without re-rendering.
🔄 Forwarding Refs and useImperativeHandle
Sometimes, components are like black boxes — you pass props in, get UI out, but can’t reach inside.
That’s fine until you need to reach inside.
Enter forwardRef and useImperativeHandle — React’s duo for exposing controlled APIs from inside child components.
🧱 forwardRef + useImperativeHandle Example
import React, { useRef, forwardRef, useImperativeHandle } from "react";
const InputBox = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focusInput: () => inputRef.current.focus(),
clearInput: () => (inputRef.current.value = ""),
}));
return <input ref={inputRef} placeholder="Enter something..." />;
});
function Parent() {
const ref = useRef(null);
return (
<div>
<InputBox ref={ref} />
<button onClick={() => ref.current.focusInput()}>Focus</button>
<button onClick={() => ref.current.clearInput()}>Clear</button>
</div>
);
}
What’s happening here:
forwardRefallows the parent to pass its ref down.useImperativeHandledefines what the parent can access — in this case, two methods.- The parent can now imperatively control the child component — just like controlling a DOM node.
Where it’s useful:
Custom inputs, modals, carousels, or third-party integrations where components must expose internal controls.
💡 When (and When NOT) to Use Refs in React
Refs are powerful — but like caffeine, they should be used with restraint.
✅ Use refs when:
- You need direct DOM manipulation (focus, scroll, measure).
- You want to store mutable values that shouldn’t cause re-renders.
- You need to interface with non-React code (Canvas, D3.js, video APIs).
- You’re building reusable UI libraries and need imperative handles.
❌ Avoid refs when:
- You can achieve the same behavior with state or props.
- You’re trying to control data flow — refs skip React’s data model.
- You’re replacing controlled inputs with uncontrolled ones unnecessarily.
👉 In short:
Use Refs for actions, not for data flow.
React’s declarative model should still drive how your UI looks and behaves.
🧠 Advanced Patterns: Custom Hooks Using useRef
When you start thinking like a senior React dev, you’ll see useRef show up inside custom hooks.
This is where it truly becomes a powerhouse.
🕵️♂️ 1. usePrevious Hook
A tiny but incredibly useful pattern for debugging or comparisons.
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Example usage:
function Demo({ count }) {
const prev = usePrevious(count);
return (
<p>
Current: {count} | Previous: {prev}
</p>
);
}
🧭 2. useClickOutside Hook
Detects clicks outside an element — used in modals, dropdowns, and popovers.
function useClickOutside(ref, callback) {
useEffect(() => {
const handleClick = (e) => {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
};
document.addEventListener("mousedown", handleClick);
return () => document.removeEventListener("mousedown", handleClick);
}, [ref, callback]);
}
// Usage
function Dropdown() {
const ref = useRef();
const [open, setOpen] = useState(false);
useClickOutside(ref, () => setOpen(false));
return (
<div ref={ref}>
<button onClick={() => setOpen(!open)}>Toggle</button>
{open && <div className="menu">Dropdown Menu</div>}
</div>
);
}
🧩 3. useDebouncedCallback Hook
Perfect for optimizing input-heavy UIs like search boxes.
function useDebouncedCallback(callback, delay) {
const timeoutRef = useRef();
const debounced = (...args) => {
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => callback(...args), delay);
};
return debounced;
}
⚡ Developer Insight
Many senior React developers use useRef not only for DOM access but for storing function references, throttling events, and tracking render counts.
It’s often called the “Swiss Army Knife” of hooks — small, silent, but immensely powerful when used wisely.
⚙️ Performance Tips and Best Practices for React Refs
Refs may look harmless — but when used carelessly, they can mess with your app’s performance or architecture. Here’s how seasoned React developers use them effectively:
✅ 1. Keep Refs Stable Across Renders
Refs created with useRef() persist between renders, so avoid re-creating them inside loops or conditionals.
// ❌ Bad
if (condition) const ref = useRef(null);
// ✅ Good
const ref = useRef(null);
Every time you re-create a ref, you lose its stored value and trigger unexpected behavior.
✅ 2. Don’t Use Refs as State Substitutes
Refs don’t trigger re-renders. That’s their superpower — and their weakness.
If you’re storing UI data (like user inputs or flags) that affects rendering, use state instead.
Use refs only for imperative actions or non-visual data (like timers or previous values).
✅ 3. Use Refs for Measuring, Not Rendering
When working with animations, responsive design, or canvas elements, refs are ideal for reading DOM sizes and positions.
Combine them with ResizeObserver or useLayoutEffect() to measure efficiently — but avoid measuring during every render, or you’ll tank performance.
✅ 4. Clean Up Side Effects Linked to Refs
Always remove event listeners or intervals tied to refs during useEffect cleanup.
It’s a common leak: developers forget, and the listener lives forever, slowing down the app.
✅ 5. Use useCallback + Refs for Function Stability
Passing ref-based callbacks into components? Wrap them with useCallback() to prevent unnecessary updates in child components.
❌ Common Pitfalls Developers Face
Even experienced developers get tripped up by Refs. Here are some of the most common mistakes (and how to dodge them):
🚫 1. Expecting Ref Changes to Trigger Re-renders
ref.current updates silently — React won’t re-render your component. If your UI depends on that value, use state instead.
🚫 2. Mixing Controlled and Uncontrolled Inputs
Developers often switch an input from being state-controlled to ref-controlled halfway. That’s a recipe for weird bugs.
Pick one pattern and stick with it.
🚫 3. Forgetting ForwardRef in Reusable Components
If you’re building reusable UI components like Input or Modal, forgetting to use forwardRef means your parent component can’t access the inner element. It’s like locking the door and throwing away the key.
🚫 4. Over-Manipulating the DOM
Refs are not a green light to revert to jQuery-style DOM hacking. If you find yourself constantly changing styles or content via refs, pause — React likely has a cleaner declarative solution.
🤔 FAQ: useRef and React Refs
🟢 Q1: Does updating a ref cause re-renders?
No. Refs are mutable and update instantly, but React doesn’t re-render when .current changes.
🟢 Q2: Is useRef the same as createRef?
Not quite. createRef creates a new ref every render (good for class components), while useRef preserves the same ref between renders (perfect for function components).
🟢 Q3: Can I use useRef to store anything, not just DOM nodes?
Absolutely. useRef can hold any mutable value — previous states, timers, cached data, even API responses.
🟢 Q4: Should I use refs instead of Redux or Context?
No. Refs are not for state management. They’re for side-effects, focus, and external integrations. Use Redux or Context for data flow.
🟢 Q5: How do refs improve app performance?
Refs help avoid unnecessary re-renders by keeping mutable data outside React’s render cycle. They’re particularly helpful in high-frequency UI updates or heavy animation contexts.
🏁 Conclusion: Why useRef Deserves a Spot in Every React Dev’s Toolkit
Here’s the truth — mastering useRef isn’t just about writing shorter code. It’s about thinking like a React engineer, not just a React user.
Every developer who grows past the “tutorial phase” eventually runs into problems that state and props alone can’t solve. That’s when useRef steps up — quiet, simple, but immensely powerful.
It’s what lets you:
- Control the DOM without breaking React’s rules.
- Integrate with animation and visualization libraries seamlessly.
- Optimize performance by avoiding redundant renders.
- Build smoother, more intuitive user experiences.
According to Stack Overflow’s 2024 developer survey, over 61% of professional React developers use useRef regularly in production — from focus management to custom hooks. That’s not a coincidence; it’s a sign of maturity in code.
So, if you’re serious about levelling up your React career — building real, production-grade apps that feel professional — learning useRef isn’t optional. It’s essential.
📚 Related Reads You’ll Love
If you enjoyed learning about state management in React, here are some other React deep-dives you shouldn’t miss 👇
- 🧩 How to Build React Forms That Actually Work (A Developer’s Real Talk)
Learn how to build forms that don’t break under pressure — validation, controlled inputs, and all the real-world gotchas. - ⚡ 🚀 What Is Redux in React? Redux Toolkit & Core Concepts Explained Simply (2025 Guide)
Everything you need to know about Redux Toolkit, modern patterns, and how it fits into React’s 2025 ecosystem. - 🎁 Props in React JS: 7 Powerful Essential Lessons You Can’t Afford to Ignore (2025 Guide)
Master how props and state work together — and why passing data the right way can save you from debugging nightmares. - 💬 JavaScript vs React JS: 7 Honest Lessons I Learned While Coding
A developer’s take on what really changes when you move from vanilla JS to the React ecosystem. - 🪄 useState in React JS: A Complete Beginner’s Guide (with Examples)
The simplest way to understand how useState works — with practical examples and mistakes to avoid. - 🎨 React JS Icons: The Complete Guide to Using Icons in React (2025)
From Material UI to React Icons — how to style, optimize, and load icons the smart way. - 🔄 React JS vs React Native: Key Differences Explained (2025)
Planning to go mobile? Understand how React Native differs from React JS before you start coding. - 🧠 JavaScript for React Developers: 7 Must-Know Skills to Finally Understand React 🧠
Before mastering React, master the JS behind it — closures, async/await, destructuring, and more. - 🪝 React Hooks: Complete Guide to useState, useEffect in React JS, and useContext (2025)
The one-stop guide to mastering the most essential React Hooks used in every modern app.