How do you type useRef()?
1. What useRef does
useRef creates a ref object ({ current: ... }),
which React keeps across renders.
The type signature:
function useRef<T>(initialValue: T): MutableRefObject<T>;2. Basic typing
const countRef = useRef<number>(0);
countRef.current++; // current is a numberTypeScript knows that countRef.current is number.
If you pass an initial value, TS infers the type automatically:
const flagRef = useRef(false); // inferred: MutableRefObject<boolean>3. If it starts out as null (DOM or lazy references)
When a ref starts out as null, you must specify the type explicitly:
// A reference to a DOM element
const inputRef = useRef<HTMLInputElement | null>(null);Here current can be null before mounting.
When using it:
inputRef.current?.focus();Safe: ?. guards against null.
4. Passing a ref into an element
export function Form() {
const inputRef = useRef<HTMLInputElement | null>(null);
return <input ref={inputRef} type="text" />;
}TypeScript already knows that inputRef.current is HTMLInputElement | null.
For different elements:
HTMLDivElementHTMLButtonElementHTMLFormElementHTMLTextAreaElementHTMLAnchorElementand so on.
5. When you only need to store a value (not DOM)
const intervalId = useRef<number | null>(null);
useEffect(() => {
intervalId.current = window.setInterval(() => console.log('tick'), 1000);
return () => clearInterval(intervalId.current!);
}, []);Useful for storing mutable variables without a re-render.
6. An example with forwardRef
type InputProps = React.ComponentProps<"input">;
const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
return <input ref={ref} {...props} />;
});
// Usage
const inputRef = useRef<HTMLInputElement>(null);
<Input ref={inputRef} />;forwardRef takes two type parameters:
React.forwardRef<ElementType, PropsType>()7. When you need useRef<T | null>(null) vs useRef<T>(...)
| Scenario | Type |
|---|---|
| A DOM element (mounts later) | useRef<T | null>(null) |
| A simple variable (number, object, etc.) | useRef<T>(value) |
| Lazy initialization | useRef<T | null>(null) |
8. A "readonly ref" (an immutable reference)
If the reference will not be changed manually,
you can use React.RefObject<T>:
const divRef: React.RefObject<HTMLDivElement> = useRef(null);RefObject = { readonly current: T | null }
Used when you just pass the ref along without modifying it.
9. An example: click outside an element
function Dropdown() {
const menuRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
function handleClick(e: MouseEvent) {
if (!menuRef.current?.contains(e.target as Node)) {
console.log("Clicked outside");
}
}
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
}, []);
return <div ref={menuRef}>Menu</div>;
}menuRef.current is typed correctly.
contains() is called safely.
Summary
| Scenario | Typing |
|---|---|
| A simple ref variable | useRef<number>(0) |
| A DOM ref | useRef<HTMLDivElement | null>(null) |
A ref on <input> | useRef<HTMLInputElement | null>(null) |
| A ref for a timer / id | useRef<number | null>(null) |
| Read-only | React.RefObject<T> |
With forwardRef | React.forwardRef<T, Props>() |
In short:
- Always add
| nullif therefis for the DOM.- Do not write
any. Use the exact element type (HTMLDivElement,HTMLInputElement, etc.).- To store "mutable" data without a re-render, use
useRef<T>().- For references between components, use
forwardRef()with a generic.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.