Mastering the React Ref API: Direct DOM Access the Right Way
Last updated 2 months, 2 weeks ago | 127 views 75 5

Introduction: Why the React Ref API Matters
In React, we usually manage UI behavior declaratively using state and props. But sometimes, we need to directly access or manipulate a DOM element, such as:
-
Setting focus on an input field
-
Scrolling to a specific element
-
Controlling media playback (video/audio)
-
Integrating with third-party libraries (e.g., charts, sliders)
That’s where the React Ref API comes in.
The Ref (short for Reference) API lets you create a persistent reference to a DOM element or component instance—bypassing the need for document.querySelector()
and keeping your logic React-friendly.
What Is the React Ref API?
React provides two main ways to create refs:
-
useRef()
– The React Hook for functional components -
createRef()
– Used in class components
Refs do not trigger re-renders. They provide a mutable container whose
.current
property can store DOM nodes or any value.
⚙️ How to Use the React Ref API
1. Using useRef
in Functional Components
import React, { useRef } from 'react';
function FocusInput() {
const inputRef = useRef(null); // Create a ref object
const handleClick = () => {
inputRef.current.focus(); // Access the input element and focus it
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Click the button to focus" />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
✅ Explanation:
-
useRef(null)
creates a reference object with.current = null
-
ref={inputRef}
connects the ref to the DOM element -
inputRef.current.focus()
invokes the DOM method directly
2. Using createRef
in Class Components
import React, { Component } from 'react';
class TextInput extends Component {
constructor() {
super();
this.inputRef = React.createRef(); // Create a ref object
}
componentDidMount() {
this.inputRef.current.focus(); // Focus input after mount
}
render() {
return <input type="text" ref={this.inputRef} />;
}
}
3. Using forwardRef
to Pass Refs to Child Components
By default, you can’t pass refs to functional child components. You need forwardRef
:
import React, { forwardRef, useRef } from 'react';
const CustomInput = forwardRef((props, ref) => (
<input ref={ref} {...props} />
));
function ParentComponent() {
const inputRef = useRef();
return (
<div>
<CustomInput ref={inputRef} placeholder="Forwarded ref input" />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</div>
);
}
4. Storing Mutable Values (Not Just DOM Nodes)
const counter = useRef(0);
// Does NOT cause a re-render when updated
counter.current += 1;
Useful when you want to keep track of values across renders without triggering re-renders—like timers, previous values, or instance variables.
Complete Working Code Example
import React, { useRef } from 'react';
function RefExampleApp() {
const inputRef = useRef(null);
const clickCounter = useRef(0); // Store mutable value
const handleClick = () => {
inputRef.current.focus(); // Focus input
clickCounter.current++;
alert(`Input focused ${clickCounter.current} times`);
};
return (
<div style={{ padding: "2rem" }}>
<h2>React Ref API Example</h2>
<input
ref={inputRef}
type="text"
placeholder="Click the button to focus me"
style={{ padding: "0.5rem", width: "300px" }}
/>
<br />
<button onClick={handleClick} style={{ marginTop: "1rem" }}>
Focus & Count Clicks
</button>
</div>
);
}
export default RefExampleApp;
✅ This demo shows:
-
Focusing an input using a ref
-
Persisting a counter without causing re-renders
Tips & Common Pitfalls
✅ Best Practices
-
Use
useRef()
for DOM manipulation, timers, and storing previous values. -
Use
forwardRef()
to expose refs from custom components. -
Use refs sparingly—prefer state and props for most interactions.
⚠️ Common Mistakes
-
Expecting changes to
ref.current
to trigger re-renders (they don’t). -
Mutating
ref.current
during rendering—it should be done in event handlers or effects. -
Forgetting to handle
null
when accessingref.current
.
Comparison: useRef vs createRef
Feature | useRef() |
createRef() |
---|---|---|
Use case | Functional components | Class components |
Re-creates on render | ❌ No | ✅ Yes (not persistent) |
Triggers re-render | ❌ No | ❌ No |
Mutability | ✅ Yes | ✅ Yes |
When to use | Preferable in modern React apps | Legacy or class-based apps only |
Conclusion: Use Refs the Right Way
The React Ref API is a powerful tool for bridging the gap between declarative React logic and imperative DOM manipulation.
Key Takeaways:
-
Use
useRef()
to access or persist values in functional components. -
Only use refs when necessary—state is still the React way.
-
forwardRef()
helps pass refs to child components. -
Avoid relying on refs for business logic or rendering decisions.
Used wisely, refs give you more control without compromising React’s declarative nature. Start small—like focusing inputs or controlling animations—and expand as needed.