Skip to content

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.
typescript
// 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

The most straightforward solution is to specify the exact HTML element type your ref will target:

typescript
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:

typescript
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:

typescript
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

ElementTypeExample
<div>HTMLDivElementuseRef<HTMLDivElement>(null)
<input>HTMLInputElementuseRef<HTMLInputElement>(null)
<button>HTMLButtonElementuseRef<HTMLButtonElement>(null)
<canvas>HTMLCanvasElementuseRef<HTMLCanvasElement>(null)
<svg>SVGSVGElementuseRef<SVGSVGElement>(null)
<iframe>HTMLIFrameElementuseRef<HTMLIFrameElement>(null)
<a>HTMLAnchorElementuseRef<HTMLAnchorElement>(null)

Understanding useRef Types

React's useRef has multiple type signatures depending on usage:

typescript
// 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:

typescript
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

  1. Always specify the correct element type rather than using the generic HTMLElement
  2. Always initialize DOM refs with null: useRef<HTMLDivElement>(null)
  3. Use generics for reusable hooks that work with multiple element types
  4. Avoid type casting unless absolutely necessary
  5. Check for null before accessing current properties:
typescript
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.