The Problem

You need a parent component to call methods on a child component or access its DOM node, but forwardRef is not working -- the ref is always null or the methods are undefined.

Symptoms

  • ref.current is null when trying to call child methods
  • TypeScript error: "Property does not exist on type ForwardedRef"
  • Parent component gets "ref.focus is not a function"
  • Ref works on native elements but not on custom components

The Root Cause

React function components do not have instances by default. You must use forwardRef to accept a ref, and useImperativeHandle to expose specific methods.

Real Error Scenario

```javascript // PARENT function Form() { const inputRef = useRef(null);

const handleSubmit = () => { inputRef.current.focus(); // ERROR: focus is not a function };

return <CustomInput ref={inputRef} />; }

// CHILD (WRONG: ref is not forwarded) function CustomInput() { return <input type="text" />; } ```

How to Fix It

Fix 1: Use forwardRef Properly

```javascript import { forwardRef, useRef, useImperativeHandle } from 'react';

const CustomInput = forwardRef((props, ref) => { const inputRef = useRef(null);

useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus(), clear: () => { inputRef.current.value = ''; }, getValue: () => inputRef.current?.value || '' }));

return <input ref={inputRef} type="text" {...props} />; });

// Parent can now call: // inputRef.current.focus() // inputRef.current.clear() // inputRef.current.getValue() ```

Fix 2: TypeScript with Proper Types

```typescript interface InputHandle { focus: () => void; clear: () => void; getValue: () => string; }

const CustomInput = forwardRef<InputHandle, InputProps>((props, ref) => { const inputRef = useRef<HTMLInputElement>(null);

useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus(), clear: () => { inputRef.current.value = ''; }, getValue: () => inputRef.current?.value || '' }));

return <input ref={inputRef} type="text" {...props} />; }); ```

Fix 3: Forward Ref to Third-Party Components

javascript
// If using a library component that doesn't forward refs,
// wrap it and attach a ref manually
const WrappedSelect = forwardRef((props, ref) => {
  const containerRef = useRef(null);
  useImperativeHandle(ref, () => ({
    element: containerRef.current
  }));
  return <div ref={containerRef}><ThirdPartySelect {...props} /></div>;
});

Common Mistakes

  • Forgetting to wrap the component with forwardRef
  • Not passing the ref to an actual DOM element or class component
  • Exposing too much via useImperativeHandle (prefer declarative APIs)
  • Using ref as a regular prop instead of the second argument of forwardRef