This TypeScript error occurs when using React.forwardRef without properly specifying generic type parameters for the ref and props types. TypeScript needs these type parameters to validate that refs are being passed correctly to the underlying component.
This error appears when you're using React's forwardRef in TypeScript without providing the necessary generic type parameters. The forwardRef function is a generic that expects two type parameters: the first specifies the type of element the ref will point to (like HTMLDivElement or HTMLButtonElement), and the second specifies the component's props type. Without these type parameters, TypeScript cannot properly infer the types and will show errors when you try to use the component with a ref prop. This is especially common when wrapping components that need to expose a ref to parent components, such as custom input components, buttons, or any component that needs imperative access to DOM elements. The forwardRef API has a unique characteristic where the order of generic parameters (ref type, then props type) is opposite to the order of function parameters (props, then ref), which can be a source of confusion for developers new to this pattern.
Specify both the ref type (element type) and props type as generic parameters to forwardRef:
import { forwardRef, ReactNode } from 'react';
// Define your props interface
interface ButtonProps {
children: ReactNode;
variant?: 'primary' | 'secondary';
onClick?: () => void;
}
// Specify <RefType, PropsType>
const MyButton = forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
return (
<button
ref={ref}
onClick={props.onClick}
className={props.variant === 'primary' ? 'btn-primary' : 'btn-secondary'}
>
{props.children}
</button>
);
}
);
MyButton.displayName = 'MyButton';
export default MyButton;The first generic parameter (HTMLButtonElement) is the type of the ref, and the second (ButtonProps) is the props type.
If you're using React.FC with forwardRef, remove it as it conflicts with forwardRef's type inference:
❌ Incorrect:
const MyInput: React.FC<InputProps> = forwardRef((props, ref) => {
// This won't work correctly
});✅ Correct:
const MyInput = forwardRef<HTMLInputElement, InputProps>(
(props, ref) => {
return <input ref={ref} {...props} />;
}
);Let forwardRef handle the typing instead of using React.FC.
When creating a forwardRef component with generics, you need to cast the result to preserve generic types:
import { forwardRef, ElementRef, ComponentPropsWithoutRef } from 'react';
// Define a generic component
interface SelectProps<T> {
value: T;
options: T[];
onChange: (value: T) => void;
}
// Cast the result to preserve generics
const Select = forwardRef(
<T,>(props: SelectProps<T>, ref: React.Ref<HTMLSelectElement>) => {
return (
<select ref={ref} value={String(props.value)}>
{props.options.map((option, i) => (
<option key={i} value={String(option)}>
{String(option)}
</option>
))}
</select>
);
}
) as <T>(
props: SelectProps<T> & { ref?: React.Ref<HTMLSelectElement> }
) => JSX.Element;
export default Select;The type assertion at the end preserves the generic type parameter T.
When extending native HTML elements, use ComponentPropsWithRef to automatically include ref types:
import { forwardRef, ComponentPropsWithRef } from 'react';
// Extend native button props
interface CustomButtonProps extends ComponentPropsWithRef<'button'> {
variant: 'primary' | 'secondary';
}
const CustomButton = forwardRef<HTMLButtonElement, CustomButtonProps>(
({ variant, children, ...props }, ref) => {
return (
<button ref={ref} className={`btn-${variant}`} {...props}>
{children}
</button>
);
}
);
CustomButton.displayName = 'CustomButton';
export default CustomButton;ComponentPropsWithRef automatically includes all standard button props and the ref type.
React 19 Improvement:
In React 19+, you can access ref directly as a prop in function components without needing forwardRef:
interface ButtonProps {
children: ReactNode;
ref?: React.Ref<HTMLButtonElement>;
}
function MyButton({ children, ref }: ButtonProps) {
return <button ref={ref}>{children}</button>;
}This simplifies the component definition significantly for projects using React 19 or later.
Custom forwardRef Declaration:
For complex generic scenarios, you can create a custom typed forwardRef wrapper:
import { forwardRef as reactForwardRef } from 'react';
function fixedForwardRef<T, P = {}>(
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
): (props: P & React.RefAttributes<T>) => React.ReactElement | null {
return reactForwardRef(render) as any;
}
// Now use fixedForwardRef instead of forwardRef for better generic supportdisplayName Convention:
Always set displayName on forwardRef components for better debugging in React DevTools:
const MyComponent = forwardRef<HTMLDivElement, Props>((props, ref) => {
// ...
});
MyComponent.displayName = 'MyComponent';Type-Only Imports:
Use type imports for better tree-shaking when importing types:
import { forwardRef } from 'react';
import type { ReactNode, Ref } from 'react';React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite