You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When `freeze` is `true`, all keyboard-driven layout changes (padding, content offset, scroll position) are paused.
199
199
200
+
### Handling a growing multiline input
201
+
202
+
If your composer has a `multiline``TextInput` that grows as the user types, you need to tell `KeyboardChatScrollView` about the extra space it takes up — otherwise the component doesn't know the scrollable range has changed and the bottom messages can get clipped under the input.
203
+
204
+
Pass a `SharedValue<number>` via the `extraContentPadding` prop. Update it in the `TextInput`'s `onLayout` callback whenever the height changes:
The value represents the **delta** above the baseline input height. When the input is at its minimum height the value is `0`; each line of growth adds to it. The component uses this value to:
242
+
243
+
1. Extend the scrollable range so the last messages aren't hidden under the taller input.
244
+
2. Scroll the list down (based on `keyboardLiftBehavior`) so the bottom messages stay visible.
245
+
246
+
:::note Using with virtualized lists
247
+
When using `extraContentPadding` with `FlatList`, `FlashList`, or `LegendList`, pass the shared value through your wrapper component — both to `KeyboardChatScrollView` and to `useCallback` dependencies so the reference is stable:
248
+
249
+
```tsx
250
+
const renderScrollComponent =useCallback(
251
+
(props:ScrollViewProps) => (
252
+
<VirtualizedListScrollView
253
+
{...props}
254
+
extraContentPadding={extraContentPadding}
255
+
/>
256
+
),
257
+
[extraContentPadding],
258
+
);
259
+
```
260
+
261
+
:::
262
+
200
263
### Reserving blank space for AI streaming responses
201
264
202
-
In AI chat apps (like ChatGPT or Claude), after the user sends a message, the AI response streams in token by token. Without special handling, the user's sent message stays at the bottom of the viewport and the streaming response grows underneath the keyboard — invisible until the user scrolls.
265
+
In AI chat apps (like ChatGPT or Claude), after the user sends a message, the AI response streams in token by token. Without special handling, the user's sent message stays at the bottom of the viewport and the streaming response grows.
203
266
204
-
The `blankSpace` prop solves this. It sets a **minimum bottom inset** on the scroll view, creating empty space below the last message. This pushes the user's sent message toward the top of the screen, leaving room for the AI response to stream in visibly. The total bottom padding is computed as:
267
+
The `blankSpace` prop solves this. It sets a **minimum bottom inset** on the scroll view, creating empty space below the last message. This allows the user's sent message being pushed toward the top of the screen, leaving room for the AI response to stream in visibly. The total bottom padding is computed as:
The value of `blankSpace` should be the amount of empty space needed below your content. In practice, this is the difference between the scrollable area and the content that sits below the "anchor" message (the last user message). A typical pattern with an inverted virtualized list looks like:
307
+
The value of `blankSpace` should be the amount of empty space needed below your content. In practice, this is the difference between the scrollable area and the content that sits below the "anchor" message (the last user message). A typical pattern with a virtualized list may looks like:
245
308
246
309
```tsx
247
310
const blankSpace =useSharedValue(0);
@@ -266,69 +329,6 @@ As the AI response streams in and grows taller, `contentBelowAnchor` increases a
266
329
On **iOS with React Native 0.81+**, the `contentInset` area created by `blankSpace` may not respond to touch/scroll gestures. To fix this, set `applyWorkaroundForContentInsetHitTestBug={true}` on `KeyboardChatScrollView`, or apply the upstream React Native patch. See the [API reference](../api/components/keyboard-chat-scroll-view#blankspace) for details.
267
330
:::
268
331
269
-
### Handling a growing multiline input
270
-
271
-
If your composer has a `multiline``TextInput` that grows as the user types, you need to tell `KeyboardChatScrollView` about the extra space it takes up — otherwise the component doesn't know the scrollable range has changed and the bottom messages can get clipped under the input.
272
-
273
-
Pass a `SharedValue<number>` via the `extraContentPadding` prop. Update it in the `TextInput`'s `onLayout` callback whenever the height changes:
The value represents the **delta** above the baseline input height. When the input is at its minimum height the value is `0`; each line of growth adds to it. The component uses this value to:
311
-
312
-
1. Extend the scrollable range so the last messages aren't hidden under the taller input.
313
-
2. Scroll the list down (based on `keyboardLiftBehavior`) so the bottom messages stay visible.
314
-
315
-
:::note Using with virtualized lists
316
-
When using `extraContentPadding` with `FlatList`, `FlashList`, or `LegendList`, pass the shared value through your wrapper component — both to `KeyboardChatScrollView` and to `useCallback` dependencies so the reference is stable:
317
-
318
-
```tsx
319
-
const renderScrollComponent =useCallback(
320
-
(props:ScrollViewProps) => (
321
-
<VirtualizedListScrollView
322
-
{...props}
323
-
extraContentPadding={extraContentPadding}
324
-
/>
325
-
),
326
-
[extraContentPadding],
327
-
);
328
-
```
329
-
330
-
:::
331
-
332
332
## Using with virtualized lists
333
333
334
334
For production chat apps you'll likely use a virtualized list (`FlatList`, `FlashList`, or `LegendList`) instead of a plain `ScrollView`. All of these accept a custom scroll component, making integration straightforward.
When `freeze` is `true`, all keyboard-driven layout changes (padding, content offset, scroll position) are paused.
199
199
200
+
### Handling a growing multiline input
201
+
202
+
If your composer has a `multiline``TextInput` that grows as the user types, you need to tell `KeyboardChatScrollView` about the extra space it takes up — otherwise the component doesn't know the scrollable range has changed and the bottom messages can get clipped under the input.
203
+
204
+
Pass a `SharedValue<number>` via the `extraContentPadding` prop. Update it in the `TextInput`'s `onLayout` callback whenever the height changes:
The value represents the **delta** above the baseline input height. When the input is at its minimum height the value is `0`; each line of growth adds to it. The component uses this value to:
242
+
243
+
1. Extend the scrollable range so the last messages aren't hidden under the taller input.
244
+
2. Scroll the list down (based on `keyboardLiftBehavior`) so the bottom messages stay visible.
245
+
246
+
:::note Using with virtualized lists
247
+
When using `extraContentPadding` with `FlatList`, `FlashList`, or `LegendList`, pass the shared value through your wrapper component — both to `KeyboardChatScrollView` and to `useCallback` dependencies so the reference is stable:
248
+
249
+
```tsx
250
+
const renderScrollComponent =useCallback(
251
+
(props:ScrollViewProps) => (
252
+
<VirtualizedListScrollView
253
+
{...props}
254
+
extraContentPadding={extraContentPadding}
255
+
/>
256
+
),
257
+
[extraContentPadding],
258
+
);
259
+
```
260
+
261
+
:::
262
+
200
263
### Reserving blank space for AI streaming responses
201
264
202
-
In AI chat apps (like ChatGPT or Claude), after the user sends a message, the AI response streams in token by token. Without special handling, the user's sent message stays at the bottom of the viewport and the streaming response grows underneath the keyboard — invisible until the user scrolls.
265
+
In AI chat apps (like ChatGPT or Claude), after the user sends a message, the AI response streams in token by token. Without special handling, the user's sent message stays at the bottom of the viewport and the streaming response grows.
203
266
204
-
The `blankSpace` prop solves this. It sets a **minimum bottom inset** on the scroll view, creating empty space below the last message. This pushes the user's sent message toward the top of the screen, leaving room for the AI response to stream in visibly. The total bottom padding is computed as:
267
+
The `blankSpace` prop solves this. It sets a **minimum bottom inset** on the scroll view, creating empty space below the last message. This allows the user's sent message being pushed toward the top of the screen, leaving room for the AI response to stream in visibly. The total bottom padding is computed as:
The value of `blankSpace` should be the amount of empty space needed below your content. In practice, this is the difference between the scrollable area and the content that sits below the "anchor" message (the last user message). A typical pattern with an inverted virtualized list looks like:
307
+
The value of `blankSpace` should be the amount of empty space needed below your content. In practice, this is the difference between the scrollable area and the content that sits below the "anchor" message (the last user message). A typical pattern with a virtualized list may looks like:
245
308
246
309
```tsx
247
310
const blankSpace =useSharedValue(0);
@@ -266,69 +329,6 @@ As the AI response streams in and grows taller, `contentBelowAnchor` increases a
266
329
On **iOS with React Native 0.81+**, the `contentInset` area created by `blankSpace` may not respond to touch/scroll gestures. To fix this, set `applyWorkaroundForContentInsetHitTestBug={true}` on `KeyboardChatScrollView`, or apply the upstream React Native patch. See the [API reference](../api/components/keyboard-chat-scroll-view#blankspace) for details.
267
330
:::
268
331
269
-
### Handling a growing multiline input
270
-
271
-
If your composer has a `multiline``TextInput` that grows as the user types, you need to tell `KeyboardChatScrollView` about the extra space it takes up — otherwise the component doesn't know the scrollable range has changed and the bottom messages can get clipped under the input.
272
-
273
-
Pass a `SharedValue<number>` via the `extraContentPadding` prop. Update it in the `TextInput`'s `onLayout` callback whenever the height changes:
The value represents the **delta** above the baseline input height. When the input is at its minimum height the value is `0`; each line of growth adds to it. The component uses this value to:
311
-
312
-
1. Extend the scrollable range so the last messages aren't hidden under the taller input.
313
-
2. Scroll the list down (based on `keyboardLiftBehavior`) so the bottom messages stay visible.
314
-
315
-
:::note Using with virtualized lists
316
-
When using `extraContentPadding` with `FlatList`, `FlashList`, or `LegendList`, pass the shared value through your wrapper component — both to `KeyboardChatScrollView` and to `useCallback` dependencies so the reference is stable:
317
-
318
-
```tsx
319
-
const renderScrollComponent =useCallback(
320
-
(props:ScrollViewProps) => (
321
-
<VirtualizedListScrollView
322
-
{...props}
323
-
extraContentPadding={extraContentPadding}
324
-
/>
325
-
),
326
-
[extraContentPadding],
327
-
);
328
-
```
329
-
330
-
:::
331
-
332
332
## Using with virtualized lists
333
333
334
334
For production chat apps you'll likely use a virtualized list (`FlatList`, `FlashList`, or `LegendList`) instead of a plain `ScrollView`. All of these accept a custom scroll component, making integration straightforward.
0 commit comments