Keyboard & Animated
These integrations are React Native only. On web, use standard DOM animation libraries or CSS transitions.
Reanimated
The Reanimated version of AnimatedLegendList supports animated props with Reanimated. Note that using Animated.createAnimatedComponent will not work as it needs more boilerplate, so you should use this instead.
Under the hood, these integrations use Reanimated.ScrollView.
Reanimated 4 sticky headers
In Reanimated 4, sticky headers can have performance problems. See Flickering/jittering while scrolling.
import { useEffect } from "react";
import { AnimatedLegendList } from "@legendapp/list/reanimated";
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from "react-native-reanimated";
export function ReanimatedExample() {
const scale = useSharedValue(0.8);
useEffect(() => {
scale.value = withSpring(1);
}, []);
return (
<AnimatedLegendList
data={data}
renderItem={renderItem}
style={useAnimatedStyle(() => ({
transform: [{ scale: scale.value }]
}))}
/>
);
}itemLayoutAnimation
Use itemLayoutAnimation to apply a Reanimated layout transition to list item containers.
import { AnimatedLegendList } from "@legendapp/list/reanimated";
import { LinearTransition } from "react-native-reanimated";
export function ReanimatedLayoutTransitionExample() {
return (
<AnimatedLegendList
data={data}
itemLayoutAnimation={LinearTransition.duration(280)}
keyExtractor={(item) => item.id}
renderItem={renderItem}
/>
);
}Animated
AnimatedLegendList supports animated props with React Native's Animated.
import { useEffect, useRef } from "react";
import { Animated } from "react-native";
import { AnimatedLegendList } from "@legendapp/list/animated";
export function AnimatedExample() {
const animated = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(animated, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}, []);
return (
<AnimatedLegendList
data={data}
renderItem={renderItem}
style={{ opacity: animated }}
/>
);
}Note that this is just a wrapper around the normal createAnimatedComponent so you can use that if you prefer.
const AnimatedLegendList = Animated.createAnimatedComponent(LegendList);KeyboardAvoidingLegendList
Use KeyboardAvoidingLegendList from @legendapp/list/keyboard for smooth keyboard-aware scrolling and inset behavior.
An experimental entrypoint is also available at @legendapp/list/keyboard-test. It currently uses KeyboardChatScrollView and is likely to replace @legendapp/list/keyboard soon.
import { KeyboardAvoidingLegendList } from "@legendapp/list/keyboard-test";This integration depends on react-native-reanimated and react-native-keyboard-controller.
npm install react-native-keyboard-controllerIntegration guidance
Do not wrap KeyboardAvoidingLegendList inside another KeyboardAvoidingView.
Let the list manage keyboard-aware behavior, and adjacent UI (like composers/inputs) should handle their own keyboard avoiding (for example with KeyboardStickyView).
Advanced customization
If your app needs more advanced keyboard-avoidance behavior, use KeyboardAvoidingLegendList as a starting point and adapt it for your scenario. See the source: src/integrations/keyboard.tsx.
Chat Example
import { useState } from "react";
import { Button, TextInput, View } from "react-native";
import { KeyboardGestureArea, KeyboardProvider, KeyboardStickyView } from "react-native-keyboard-controller";
import { useAnimatedScrollHandler } from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { KeyboardAvoidingLegendList } from "@legendapp/list/keyboard";
export function KeyboardAvoidingExample() {
const [messages, setMessages] = useState(defaultChatMessages);
const [inputText, setInputText] = useState("");
const insets = useSafeAreaInsets();
const sendMessage = () => {
const text = inputText || "Empty message";
if (text.trim()) {
setMessages((messagesNew) => [
...messagesNew,
{ id: String(idCounter++), sender: "user", text: text, timeStamp: Date.now() },
]);
setInputText("");
}
};
const handleScroll = useAnimatedScrollHandler({
onScroll: (_event) => {},
});
return (
<KeyboardProvider>
<View style={[styles.container, { paddingBottom: insets.bottom, paddingTop: insets.top }]}>
<KeyboardGestureArea interpolator="ios" offset={60} style={styles.container}>
<KeyboardAvoidingLegendList
alignItemsAtEnd
contentContainerStyle={styles.contentContainer}
data={messages}
estimatedItemSize={80}
initialScrollAtEnd
keyExtractor={(item) => item.id}
maintainScrollAtEnd
maintainVisibleContentPosition
onScroll={handleScroll}
renderItem={ChatMessage}
safeAreaInsetBottom={insets.bottom}
style={styles.list}
/>
</KeyboardGestureArea>
<KeyboardStickyView offset={{ closed: 0, opened: insets.bottom }}>
<View style={styles.inputContainer}>
<TextInput
onChangeText={setInputText}
placeholder="Type a message"
style={styles.input}
value={inputText}
/>
<Button onPress={sendMessage} title="Send" />
</View>
</KeyboardStickyView>
</View>
</KeyboardProvider>
);
}