> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/preactjs/preact/llms.txt
> Use this file to discover all available pages before exploring further.

# useImperativeHandle

> Customizes the instance value exposed to parent components when using refs

`useImperativeHandle` customizes the instance value that is exposed to parent components when using `ref`. This hook should be used with `forwardRef`.

## Signature

```typescript theme={null}
function useImperativeHandle<T, R extends T>(
  ref: Ref<T>,
  create: () => R,
  inputs?: ReadonlyArray<unknown>
): void
```

## Parameters

<ParamField path="ref" type="Ref<T>" required>
  The ref that will be mutated. This is typically the ref forwarded from a parent component.
</ParamField>

<ParamField path="create" type="() => R" required>
  A function that returns the value to be attached to `ref.current`. This should return an object with the methods/properties you want to expose.
</ParamField>

<ParamField path="inputs" type="ReadonlyArray<unknown>" optional>
  An array of dependencies. The effect will only activate if the values in this array change (compared using `===`). If omitted, the handle is recreated on every render.
</ParamField>

## Returns

`void`

## Basic Usage

```jsx theme={null}
import { forwardRef, useImperativeHandle, useRef } from 'preact/compat';
import { useRef as useHookRef } from 'preact/hooks';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useHookRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    clear: () => {
      inputRef.current.value = '';
    }
  }), []);

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

function Parent() {
  const inputRef = useHookRef();

  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
      <button onClick={() => inputRef.current.clear()}>Clear</button>
    </div>
  );
}
```

## Video Player Control

```jsx theme={null}
const VideoPlayer = forwardRef(({ src }, ref) => {
  const videoRef = useRef();

  useImperativeHandle(ref, () => ({
    play: () => {
      videoRef.current.play();
    },
    pause: () => {
      videoRef.current.pause();
    },
    seek: (time) => {
      videoRef.current.currentTime = time;
    },
    setVolume: (volume) => {
      videoRef.current.volume = Math.max(0, Math.min(1, volume));
    },
    getCurrentTime: () => {
      return videoRef.current.currentTime;
    }
  }), []);

  return <video ref={videoRef} src={src} />;
});

function VideoController() {
  const playerRef = useRef();

  return (
    <div>
      <VideoPlayer ref={playerRef} src="video.mp4" />
      <button onClick={() => playerRef.current.play()}>Play</button>
      <button onClick={() => playerRef.current.pause()}>Pause</button>
      <button onClick={() => playerRef.current.seek(0)}>Restart</button>
      <button onClick={() => playerRef.current.setVolume(0.5)}>50% Volume</button>
    </div>
  );
}
```

## Form Control

```jsx theme={null}
const Form = forwardRef(({ children }, ref) => {
  const formRef = useRef();
  const [errors, setErrors] = useState({});

  useImperativeHandle(ref, () => ({
    submit: () => {
      formRef.current.dispatchEvent(
        new Event('submit', { cancelable: true, bubbles: true })
      );
    },
    reset: () => {
      formRef.current.reset();
      setErrors({});
    },
    validate: () => {
      const isValid = formRef.current.checkValidity();
      if (!isValid) {
        // Collect validation errors
        const newErrors = {};
        const elements = formRef.current.elements;
        for (let i = 0; i < elements.length; i++) {
          const el = elements[i];
          if (!el.validity.valid) {
            newErrors[el.name] = el.validationMessage;
          }
        }
        setErrors(newErrors);
      }
      return isValid;
    },
    getValues: () => {
      const formData = new FormData(formRef.current);
      return Object.fromEntries(formData.entries());
    }
  }), []);

  return <form ref={formRef}>{children}</form>;
});

function App() {
  const formRef = useRef();

  const handleExternalSubmit = () => {
    if (formRef.current.validate()) {
      const values = formRef.current.getValues();
      console.log('Form values:', values);
      formRef.current.submit();
    }
  };

  return (
    <div>
      <Form ref={formRef}>
        <input name="email" type="email" required />
        <input name="password" type="password" required />
      </Form>
      <button onClick={handleExternalSubmit}>Submit</button>
      <button onClick={() => formRef.current.reset()}>Reset</button>
    </div>
  );
}
```

## Modal Control

```jsx theme={null}
const Modal = forwardRef(({ title, children }, ref) => {
  const [isOpen, setIsOpen] = useState(false);

  useImperativeHandle(ref, () => ({
    open: () => setIsOpen(true),
    close: () => setIsOpen(false),
    toggle: () => setIsOpen(prev => !prev),
    isOpen: () => isOpen
  }), [isOpen]);

  if (!isOpen) return null;

  return (
    <div className="modal-overlay">
      <div className="modal">
        <h2>{title}</h2>
        <div>{children}</div>
        <button onClick={() => setIsOpen(false)}>Close</button>
      </div>
    </div>
  );
});

function App() {
  const modalRef = useRef();

  return (
    <div>
      <button onClick={() => modalRef.current.open()}>Open Modal</button>
      <Modal ref={modalRef} title="My Modal">
        <p>Modal content here</p>
      </Modal>
    </div>
  );
}
```

## Canvas Drawing API

```jsx theme={null}
const Canvas = forwardRef(({ width, height }, ref) => {
  const canvasRef = useRef();

  useImperativeHandle(ref, () => {
    const ctx = canvasRef.current?.getContext('2d');
    return {
      clear: () => {
        ctx.clearRect(0, 0, width, height);
      },
      drawCircle: (x, y, radius, color = 'black') => {
        ctx.beginPath();
        ctx.arc(x, y, radius, 0, 2 * Math.PI);
        ctx.fillStyle = color;
        ctx.fill();
      },
      drawRect: (x, y, w, h, color = 'black') => {
        ctx.fillStyle = color;
        ctx.fillRect(x, y, w, h);
      },
      drawLine: (x1, y1, x2, y2, color = 'black', width = 1) => {
        ctx.strokeStyle = color;
        ctx.lineWidth = width;
        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        ctx.stroke();
      },
      getImageData: () => {
        return canvasRef.current.toDataURL();
      }
    };
  }, [width, height]);

  return <canvas ref={canvasRef} width={width} height={height} />;
});

function DrawingApp() {
  const canvasRef = useRef();

  return (
    <div>
      <Canvas ref={canvasRef} width={800} height={600} />
      <button onClick={() => canvasRef.current.drawCircle(100, 100, 50, 'red')}>
        Draw Circle
      </button>
      <button onClick={() => canvasRef.current.clear()}>Clear</button>
    </div>
  );
}
```

## With Dependencies

```jsx theme={null}
const Counter = forwardRef(({ step = 1 }, ref) => {
  const [count, setCount] = useState(0);

  useImperativeHandle(ref, () => ({
    increment: () => setCount(c => c + step),
    decrement: () => setCount(c => c - step),
    reset: () => setCount(0),
    getValue: () => count
  }), [step, count]); // Recreate when step or count changes

  return <div>Count: {count}</div>;
});
```

<Note>
  `useImperativeHandle` should be used with `forwardRef`. It allows child components to expose a custom API to parent components through refs.
</Note>

<Note>
  This hook runs during the layout phase (like `useLayoutEffect`), so the ref value is updated synchronously before the browser paints.
</Note>

<Note>
  Avoid overusing imperative APIs. Most interactions between components should be done declaratively through props. Use `useImperativeHandle` sparingly for cases where you need to expose imperative methods like `focus()`, `play()`, or `reset()`.
</Note>
