react-native-streaming-message-list
v0.0.4
Published
React Native message list with ChatGPT or Claude-style smart scrolling for incremental/streaming-like updates
Maintainers
Readme
Features
- Smart scroll behavior: New messages snap to top with dynamic blank space management
- Streaming-friendly: Handles growing/updating content without scroll jank
- Optional animations: Built-in slide-up and fade-in animations for new messages
- FlatList-like API: Familiar props, works with any message data structure
Installation
npm install react-native-streaming-message-list react-native-reanimatedThis library requires react-native-reanimated. Follow their installation guide if you haven't already.
Quick Start
💡 For a complete working example, check out the example folder.
1. Replace your list component
Replace FlatList with StreamingMessageList. This component is built on @legendapp/list and accepts the same FlatList-like props:
- import { FlatList } from 'react-native';
+ import { StreamingMessageList } from 'react-native-streaming-message-list';
- <FlatList
+ <StreamingMessageList
data={messages}
keyExtractor={(item) => item.id}
renderItem={renderMessage}
/>2. Add streaming state
Create a state variable to track when messages are actively streaming:
const [isStreaming, setIsStreaming] = useState(false);Pass it to StreamingMessageList:
<StreamingMessageList
data={messages}
keyExtractor={(item) => item.id}
renderItem={renderMessage}
+ isStreaming={isStreaming}
/>3. Wrap your anchor and streaming items
To enable smart scrolling, wrap two special messages:
- Last user message: Wrap with
AnchorItem(this message will stay near the top) - Streaming assistant message: Wrap with
StreamingItem(the growing response)
import { AnchorItem, StreamingItem } from 'react-native-streaming-message-list';
const renderMessage = ({ item, index }) => {
const isLastUserMessage =
item.role === 'user' && index === messages.length - 1;
const isStreamingMessage = item.role === 'assistant' && isStreaming;
let content = <YourMessageBubble message={item} />;
if (isLastUserMessage) {
content = <AnchorItem>{content}</AnchorItem>;
} else if (isStreamingMessage) {
content = <StreamingItem>{content}</StreamingItem>;
}
return content;
};That's it! The list will now handle ChatGPT-style scrolling automatically.
Need more? See the example folder for a complete, runnable chat app with streaming simulation.
Common Patterns
When to use each component
StreamingMessageList: Your main list component. Use it instead ofFlatListfor any chat/message list where content can stream or grow.AnchorItem: Wrap the last user message before a streaming response begins. This keeps it visible near the top while the assistant response grows below it.StreamingItem: Wrap the currently growing/streaming message (typically the last assistant message). This enables smooth scroll tracking as content updates.AnimatedMessage: Optional animated wrapper for new messages. SupportsslideUp,fadeIn, ornoneanimations:
import { AnimatedMessage } from 'react-native-streaming-message-list';
<AnimatedMessage
animation="slideUp"
onAnimationComplete={() => console.log('done')}
>
<YourMessageBubble />
</AnimatedMessage>;Typical message flow
- User sends a message → mark it as
AnchorItem - Assistant starts responding → set
isStreaming={true}and wrap the new assistant message withStreamingItem - Assistant finishes → set
isStreaming={false} - Repeat for the next turn
API
<StreamingMessageList>
Main component that wraps your message list with smart scroll behavior.
Props
Extends all FlatList props from @legendapp/list, plus:
| Prop | Type | Required | Description |
| -------------- | ---------------------------- | -------- | ------------------------------------------------------------- |
| data | T[] | Yes | Array of message items |
| renderItem | (info) => ReactNode | Yes | Function to render each item |
| keyExtractor | (item, index) => string | Yes | Unique key for each item |
| isStreaming | boolean | No | Whether content is currently updating (triggers smart scroll) |
| config | StreamingMessageListConfig | No | Advanced configuration |
Config Options
type StreamingMessageListConfig = {
debounceMs?: number; // Debounce for placeholder height calculations (default: 150)
placeholderStableDelayMs?: number; // Delay before placeholder is considered stable (default: 200)
};<AnchorItem>
Wrapper for the message that should be "anchored" near the top when a new conversation turn begins (typically the last user message).
<AnchorItem>
<YourMessageBubble />
</AnchorItem><StreamingItem>
Wrapper for content that's actively growing/updating (typically the last assistant message while streaming).
<StreamingItem>
<YourMessageBubble />
</StreamingItem><AnimatedMessage>
Optional animated wrapper for new messages.
Props
| Prop | Type | Description |
| --------------------- | --------------------------------- | -------------------------------- |
| animation | 'slideUp' \| 'fadeIn' \| 'none' | Animation type |
| onAnimationComplete | () => void | Callback when animation finishes |
| children | ReactNode | Content to animate |
How It Works
The component implements ChatGPT-style scrolling by:
- Measuring heights: Tracks the "anchor" message (last user message) and "streaming" content (growing assistant response)
- Dynamic placeholder: Injects blank space at the bottom so the anchor message lands near the top
- Auto-scrolling: Automatically scrolls to show new messages
- Debounced updates: Prevents jank during rapid content updates
Contributing
See CONTRIBUTING.md
License
MIT
Made with create-react-native-library
