Understanding useRef and forwardRef in React — A Beginner’s Guide
When working with React, you often hear about useRef and forwardRef.
At first, they can feel confusing: “Why do I need refs when I already have props and state?”
This article will walk you through the why, how, and when of refs in React — with simple examples, analogies, and real use cases.
1. What is useRef?
Think of useRef as a sticky note inside your component.
Whatever you write on it will still be there even after React re-renders your component.
function App() {
const inputRef = React.useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus();
};
return (
<div>
<input ref={inputRef} placeholder="Type here..." />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
useRefcreates a box that holds a reference to the <input> DOM element.inputRef.currentpoints to the real HTML element.- Clicking the button calls
.focus()on the input directly.
Key point: useRef is not just for inputs. You can store any value inside it that survives re-renders. But the most common use is working with DOM elements.
2. What is forwardRef?
Refs work perfectly on built-in HTML tags (<input>, <button>, etc.). But what if you wrap an <input> in your own custom component?
function MyInput(props: React.InputHTMLAttributes<HTMLInputElement>) {
return <input {...props} />;
}
function App() {
const inputRef = React.useRef<HTMLInputElement>(null);
return <MyInput ref={inputRef} />; // ❌ doesn't work
}
Here, the parent tries to pass a ref, but it stops at MyInput.
React doesn’t know how to forward refs into custom function components.
That’s where forwardRef comes in.
3. How forwardRef Works
const MyInput = React.forwardRef<
HTMLInputElement,
React.InputHTMLAttributes<HTMLInputElement>
>((props, ref) => {
return <input {...props} ref={ref} />;
});
MyInput.displayName = "MyInput";
function App() {
const inputRef = React.useRef<HTMLInputElement>(null);
React.useEffect(() => {
inputRef.current?.focus(); // ✅ Works
}, []);
return <MyInput ref={inputRef} placeholder="Type here..." />;
}
Now the parent’s ref goes directly to the <input> inside MyInput.
4. Visualizing the Flow
Without forwardRef:
Parent ──ref──▶ [ MyInput ] ✖ stops here
└── <input> (unreachable)
With forwardRef:
Parent ──ref──▶ [ MyInput (forwardRef) ] ──▶ <input ref={ref}> ✅
Think of a ref as a power cable:
- Without
forwardRef, the cable gets stuck outside the box. - With
forwardRef, you drill a hole so the cable reaches the actual device inside.
5. Why We Need Both
useRef= creates the cable (a handle to a DOM node).forwardRef= ensures your custom component passes the cable through.
They’re often used together when you build reusable UI components like inputs, buttons, or dropdowns.
6. Real-World Use Cases
- Focus an input →
ref.current?.focus() - Scroll to an element →
ref.current?.scrollIntoView() - Measure size/position →
ref.current?.getBoundingClientRect() - Work with libraries → pass refs into custom components for
react-hook-form,framer-motion, etc.
7. Beginner-Friendly Analogy
useRef= a walkie-talkie to talk directly to a DOM element.forwardRef= a cable extension so custom components don’t block the signal.
8. Takeaway
- Use
useRefto grab DOM elements or store values across renders. - Use
forwardRefwhen building custom components that wrap DOM elements.
With these two tools, your React components stay flexible, reusable, and library-friendly.
9. useRef vs useState
At first glance, both seem to “store values.” But they behave very differently.
useState
- Triggers a re-render when the value changes.
- Best for values that affect the UI.
function Counter() {
const [count, setCount] = React.useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
useRef
- Does not trigger re-renders when value changes.
- Best for mutable values or DOM nodes.
function Stopwatch() {
const timeRef = React.useRef(0);
const tick = () => {
timeRef.current += 1;
console.log("Current time:", timeRef.current);
};
return <button onClick={tick}>Tick</button>;
}
Comparison Table
| Feature | useState |
useRef |
|---|---|---|
| Causes re-render? | ✅ Yes | ❌ No |
| Stores values across renders? | ✅ Yes | ✅ Yes |
| Best for | UI data (things you want to show) | DOM elements, timers, mutable values |
| Example use | Counter, form fields | Focus, scroll, intervalId |
Rule of Thumb
- If you want the UI to update when the value changes → use
useState. - If you just need to hold a value or DOM node → use
useRef.
Final Thoughts
- useRef is your silent helper: great for DOM access and values that don’t need UI updates.
- useState is your loud announcer: changes always trigger re-renders.
- forwardRef makes custom components ref-friendly, so they behave like native DOM elements.
Together, these hooks are the glue between React’s declarative UI world and the imperative needs of DOM manipulation.
Comments
Post a Comment