useRef TypeScript Error: Not Assignable to LegacyRef
Problem Statement
When using React's useRef
hook with TypeScript, developers often encounter the error:
Type 'RefObject<HTMLElement>' is not assignable to type 'LegacyRef<HTMLDivElement> | undefined'
This commonly occurs when trying to use a generic HTMLElement
type reference with a specific HTML element like a <div>
, <input>
, or <canvas>
.
The Core Issue
The error stems from a type mismatch between what your ref provides and what the React element expects:
- Your ref: Returns
HTMLElement
(general parent class) - React element: Expects a specific element type like
HTMLDivElement
,HTMLInputElement
, etc.
// This creates the type mismatch
const node = useRef<HTMLElement | null>(null);
return <div ref={node}>Content</div>; // Error!
TypeScript prevents this because an HTMLElement
doesn't guarantee all the properties and methods that a specific element type like HTMLDivElement
would have.
Solutions
1. Use the Correct Element Type (Recommended)
The most straightforward solution is to specify the exact HTML element type your ref will target:
import React, { useRef } from 'react';
function Component() {
const divRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
const svgRef = useRef<SVGSVGElement>(null);
const iframeRef = useRef<HTMLIFrameElement>(null);
return (
<>
<div ref={divRef}>Content</div>
<input ref={inputRef} />
<canvas ref={canvasRef} />
<svg ref={svgRef} />
<iframe ref={iframeRef} />
</>
);
}
WARNING
Always initialize with null
: useRef<HTMLDivElement>(null)
2. Generic Hook Pattern for Reusable Components
For reusable hooks that need to work with different element types, use generics:
const useGenericHook = <T extends HTMLElement = HTMLDivElement>() => {
const elementRef = useRef<T>(null);
// Your custom logic here
return elementRef;
};
function MyComponent() {
const buttonRef = useGenericHook<HTMLButtonElement>();
const linkRef = useGenericHook<HTMLAnchorElement>();
const divRef = useGenericHook(); // Defaults to HTMLDivElement
return (
<>
<button ref={buttonRef}>Click</button>
<a ref={linkRef}>Link</a>
<div ref={divRef}>Content</div>
</>
);
}
3. Type Casting (Use with Caution)
If you absolutely need to use a generic ref, you can cast it to the appropriate type:
const node = useRef<HTMLElement>(null);
return <div ref={node as React.RefObject<HTMLDivElement>}>Content</div>;
DANGER
Type casting bypasses TypeScript's type checking and may lead to runtime errors if the element doesn't match the expected type.
Common Element Types Reference
Element | Type | Example |
---|---|---|
<div> | HTMLDivElement | useRef<HTMLDivElement>(null) |
<input> | HTMLInputElement | useRef<HTMLInputElement>(null) |
<button> | HTMLButtonElement | useRef<HTMLButtonElement>(null) |
<canvas> | HTMLCanvasElement | useRef<HTMLCanvasElement>(null) |
<svg> | SVGSVGElement | useRef<SVGSVGElement>(null) |
<iframe> | HTMLIFrameElement | useRef<HTMLIFrameElement>(null) |
<a> | HTMLAnchorElement | useRef<HTMLAnchorElement>(null) |
Understanding useRef Types
React's useRef
has multiple type signatures depending on usage:
// For mutable values (not DOM elements)
function useRef<T>(initialValue: T): MutableRefObject<T>;
// For DOM elements (initialized with null)
function useRef<T>(initialValue: T|null): RefObject<T>;
// Without initial value
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
The RefObject<T>
interface is defined as:
interface RefObject<T> {
readonly current: T | null;
}
This is why you don't need to explicitly include | null
in your type parameter - TypeScript already knows that current
might be null.
Best Practices
- Always specify the correct element type rather than using the generic
HTMLElement
- Always initialize DOM refs with null:
useRef<HTMLDivElement>(null)
- Use generics for reusable hooks that work with multiple element types
- Avoid type casting unless absolutely necessary
- Check for null before accessing
current
properties:
if (ref.current) {
// Safe to access ref.current properties
ref.current.focus(); // For input elements
ref.current.getContext('2d'); // For canvas elements
}
By following these patterns, you'll avoid type errors while maintaining type safety and code reliability.