SIGN IN SIGN UP

fix: strict api compatibility (#1560)

## 📜 Description

Fixed compatibility of
`KeyboardAwareScrollView`/`KeyboardChatScrollView` with
`react-native-strict-api`.

## đź’ˇ Motivation and Context

<!-- Why is this change required? What problem does it solve? -->
<!-- If it fixes an open issue, please link to the issue here. -->

React Native’s `react-native-strict-api` export condition exposes
`ScrollView` as a function component rather than a class. Our existing
ref types relied on class-instance typing:

- `KeyboardAwareScrollViewRef` intersected with `ScrollView`
- `KeyboardChatScrollView` exposed Reanimated’s `AnimatedScrollView` ref
type, which internally relies on the same pattern

Under strict API, those types lose imperative methods such as
`scrollTo`, `scrollToEnd`, and `measure`. The runtime ref is still
valid; this is a TypeScript declaration issue.

This change derives the native `ScrollView` instance type with
`React.ComponentRef<typeof ScrollView>` and exposes it as
`KeyboardChatScrollViewRef`. It keeps the public ref API correct in both
classic and strict-API React Native typings.

Possible alternatives:

- Keep `React.ElementRef<typeof KeyboardChatScrollView>` in consumers:
this works only when the component’s own public ref declaration is
correct. It cannot fix the broken inferred ref type under strict API.
- Type the ref as `ScrollView`: this works with classic React Native
declarations but fails under strict API because `ScrollView` becomes a
component type, not its instance type.
- Expose Reanimated’s `AnimatedScrollView`: this preserves the existing
issue because Reanimated’s type is built on the classic `ScrollView`
class shape.
- Use consumer-side casts or wrapper components: this is a workaround,
but pushes library typing details to every consumer.
- Fix Reanimated’s declarations upstream: worthwhile independently, but
does not remove our responsibility to expose a stable public ref type.

We chose `ComponentRef<typeof ScrollView>` because it resolves to the
actual imperative instance in both typing modes, matches the runtime ref
shape, and provides a simple exported type for consumers:
`useRef<KeyboardChatScrollViewRef>(null)`.

Closes
https://github.com/kirillzyusko/react-native-keyboard-controller/issues/1559

## 📢 Changelog

<!-- High level overview of important changes -->
<!-- For example: fixed status bar manipulation; added new types
declarations; -->
<!-- If your changes don't affect one of platform/language below - then
remove this platform/language -->

### JS

- added `KeyboardChatScrollViewRef` (similar to
`KeyboardAwareScrollViewRef`);
- change ref signatures so that they are compatible with both strict and
non-strict API;

### Docs

- use  `KeyboardChatScrollViewRef` instead of self derived ref;

## 🤔 How Has This Been Tested?

Tested with following code:

```tsx
import { useRef } from "react";
import {
  KeyboardAwareScrollView,
  KeyboardChatScrollView,
} from "react-native-keyboard-controller";

import type { KeyboardAwareScrollViewRef } from "react-native-keyboard-controller";

type KeyboardChatScrollViewRef = React.ElementRef<
  typeof KeyboardChatScrollView
>;

export function StrictApiReproduction() {
  const ref = useRef<KeyboardAwareScrollViewRef>(null);

  return (
    <KeyboardAwareScrollView
      ref={ref}
      onLayout={() => {
        ref.current?.assureFocusedInputVisible();
        ref.current?.scrollTo({ animated: true, y: 0 });
        ref.current?.scrollToEnd({ animated: true });
      }}
    />
  );
}

export function StrictApiKeyboardChatScrollViewReproduction() {
  const ref = useRef<KeyboardChatScrollViewRef>(null);

  return (
    <KeyboardChatScrollView
      ref={ref}
      onLayout={() => {
        ref.current?.scrollTo({ animated: true, y: 0 });
        ref.current?.scrollToEnd({ animated: true });
      }}
    />
  );
}
```

And following `tsconfig.json` in `example`:

```json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "customConditions": ["react-native-strict-api", "react-native"]
  },
  "include": ["**/*.ts", "**/*.tsx"],
  "exclude": ["**/node_modules", "**/Pods"]
}
```

## 📸 Screenshots (if appropriate):

### KeyboardAwareScrollView

|Before|After|
|-------|-----|
|<img width="1088" height="373" alt="Screenshot 2026-07-21 at 10 46 32"
src="https://github.com/user-attachments/assets/80fd86e9-d4d2-41cf-a221-54a0d50a87cd"
/>|<img width="726" height="370" alt="Screenshot 2026-07-21 at 10 48 17"
src="https://github.com/user-attachments/assets/73232e16-b9d9-46fa-b265-7cc0a92d7851"
/>|

### KeyboardChatScrollView

|Before|After|
|-------|-----|
|<img width="1323" height="347" alt="Screenshot 2026-07-21 at 11 16 31"
src="https://github.com/user-attachments/assets/de3ea478-a76e-4a96-b24b-9d2d99c8cbbd"
/>|<img width="736" height="339" alt="Screenshot 2026-07-21 at 11 02 20"
src="https://github.com/user-attachments/assets/bb154d29-bff4-4add-80d0-f92f2cd091e4"
/>|

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
K
Kirill Zyusko committed
2bb2323ee170cb6478a3b6f560688eae478d2109
Parent: d29b28d
Committed by GitHub <noreply@github.com> on 7/21/2026, 10:04:16 AM