You've got a custom <Input /> or <TextField /> component, you drop {...register('email')} on it, and TypeScript screams TS2769 at you. No overload matches this call. The overload list is useless — forty lines of JSX.IntrinsicElements signatures that don't apply. This shows up almost immediately after you switch from a raw <input> to your own wrapper, or when you paste a snippet from the RHF docs into a styled component.
It's rarely about your form types. It's about what your component actually accepts as props.
What's actually going wrong
register('email') returns a UseFormRegisterReturn object. That object has four keys: name, onChange, onBlur, and ref. When you spread it onto a native <input>, TypeScript is happy because HTMLInputElement accepts all of those.
Your custom component almost certainly doesn't. Maybe it's typed as { label: string; placeholder?: string }. Maybe it extends React.ComponentPropsWithoutRef<'input'> but drops ref. Either way, TypeScript sees a ref key on the spread object and no matching prop on your component, and it falls through every overload trying to find one that fits. Nothing fits. TS2769.
The other common trigger: you're on React 18 and your component is a function that returns JSX but never touches forwardRef. Plain function components don't accept ref as a prop. Period. React itself will warn at runtime, and TypeScript catches it first.
The fix
-
Wrap the component in
forwardRefand type the ref as the underlying DOM element. If you're wrapping a native input, that'sHTMLInputElement. If you're wrapping a library component like MUI's TextField, it'sHTMLInputElementtoo, but you'll need to forward the ref through toinputRefbecause TextField uses that prop name instead ofref.import { forwardRef, InputHTMLAttributes } from 'react'; interface InputProps extends InputHTMLAttributes<HTMLInputElement> { label?: string; } export const Input = forwardRef<HTMLInputElement, InputProps>>( ({ label, ...rest }, ref) => ( <label> {label} <input ref={ref} {...rest} /> </label> ) ); Input.displayName = 'Input';Note the props type. Extending
InputHTMLAttributes<HTMLInputElement>is what pulls inname,onChange,onBlur, and everything else RHF's return object contains. Skip that and you'll get a different error — usually TS2322 on the spread. -
If you're on React 19, drop
forwardRef. Ref is a regular prop now. This is the one case where the modern React docs are actually cleaner than the old pattern:interface InputProps extends React.ComponentProps<'input'> { label?: string; } export function Input({ label, ref, ...rest }: InputProps) { return ( <label> {label} <input ref={ref} {...rest} /> </label> ); }Mixed React versions in a monorepo is a recipe for confusion here. Check your root
package.jsonbefore you pick one. -
Make sure the component's prop type doesn't narrow
onChange. I've seen people writeonChange?: (value: string) => voidon their custom input. That's not the DOM signature. RHF passes aChangeEventHandler— it hands you an event, not a value. If your component expects a value, TS2769 comes right back because the spread'sonChangedoesn't match. UseChangeEventHandler<HTMLInputElement>, or striponChangeout and call it manually, but don't try to make RHF speak your custom API. -
For MUI, Chakra, or any wrapper that renames the ref, forward it correctly. MUI's
TextFieldtakesinputRef, notref. If you're writing a wrapper around it, the pattern is:export const WrappedField = forwardRef<HTMLInputElement, Props>( (props, ref) => <TextField {...props} inputRef={ref} /> );Forgetting
inputRefhere means RHF never gets a handle on the actual input, and yourformState.errorswon't clear on focus. That's a runtime bug, not a TypeScript one, but it starts with the same confusion.
When it still fails
- Check your
tsconfigstrictness. IfstrictFunctionTypesis off, some of these slips through silently in dev and blows up in CI. Turn it on and fix it once. - Look at the actual overload error, not the first one. TypeScript prints candidates in order. Scroll to the last few — they're usually the JSX-intrinsic ones and they tell you which prop didn't match.
- Don't cast with
as any. It hides the problem and you'll hit a runtimeref is not a functionwhen RHF tries to focus the field on submit. Fix the component. - Verify you're not passing
register()to a wrapper that renders conditionally. If your component returnsnullwhendisabledis set, RHF's ref callback never fires. That's a separate issue but it masquerades as a typing problem. - Duplicate @types/react versions. Run
npm ls @types/react. Two majors in the tree will makeJSX.IntrinsicElementsresolve to two different types and TS2769 is a classic symptom. Dedupe with your package manager's override field.
The real fix is almost always step one: the component needs to actually accept the props register() returns, and it needs to be forwardRef (or React 19). Don't fight the types. Match them.