React Native’s LayoutAnimation API provides a simple way to animate layout changes. FlashList supports LayoutAnimation, but requires a specific setup to work correctly with view recycling.
LayoutAnimation is experimental on Android and may have stability issues.
Quick Start
To use LayoutAnimation with FlashList, you must call prepareForLayoutAnimationRender() before triggering the animation:
import React , { useRef , useState } from "react" ;
import { View , Text , Pressable , LayoutAnimation } from "react-native" ;
import { FlashList } from "@shopify/flash-list" ;
const AnimatedList = () => {
const [ data , setData ] = useState ([ 1 , 2 , 3 , 4 , 5 ]);
const listRef = useRef < FlashList < number >>( null );
const removeItem = ( item : number ) => {
// 1. Update the data
setData (( current ) => current . filter (( i ) => i !== item ));
// 2. Prepare FlashList for animation (BEFORE LayoutAnimation.configureNext)
listRef . current ?. prepareForLayoutAnimationRender ();
// 3. Configure the animation
LayoutAnimation . configureNext ( LayoutAnimation . Presets . easeInEaseOut );
};
return (
< FlashList
ref = { listRef }
data = { data }
keyExtractor = { ( item ) => item . toString () }
renderItem = { ({ item }) => (
< Pressable onPress = { () => removeItem ( item ) } >
< View style = { { padding: 20 , backgroundColor: "#fff" } } >
< Text > Item { item } - Tap to remove </ Text >
</ View >
</ Pressable >
) }
/>
);
};
Step-by-Step Guide
Create a ref to your FlashList
Use useRef to maintain a reference to your FlashList instance: import { useRef } from "react" ;
import { FlashList } from "@shopify/flash-list" ;
const listRef = useRef < FlashList < YourDataType >>( null );
< FlashList
ref = { listRef }
// ... other props
/>
Add keyExtractor prop
The keyExtractor prop is required for animations to work properly: < FlashList
ref = { listRef }
data = { data }
keyExtractor = { ( item ) => item . id . toString () }
renderItem = { renderItem }
/>
Without keyExtractor, animations will not work correctly because FlashList cannot track which items have been added or removed.
Call prepareForLayoutAnimationRender
Before calling LayoutAnimation.configureNext, call prepareForLayoutAnimationRender(): const addItem = () => {
setData ([ ... data , newItem ]);
listRef . current ?. prepareForLayoutAnimationRender ();
LayoutAnimation . configureNext ( LayoutAnimation . Presets . easeInEaseOut );
};
This method must be called before LayoutAnimation.configureNext for the animation to render properly.
Configure your animation
Use any of React Native’s LayoutAnimation presets or create custom configurations: // Using presets
LayoutAnimation . configureNext ( LayoutAnimation . Presets . spring );
// Custom configuration
LayoutAnimation . configureNext ({
duration: 300 ,
create: {
type: LayoutAnimation . Types . easeInEaseOut ,
property: LayoutAnimation . Properties . opacity ,
},
update: {
type: LayoutAnimation . Types . spring ,
springDamping: 0.7 ,
},
delete: {
type: LayoutAnimation . Types . easeInEaseOut ,
property: LayoutAnimation . Properties . opacity ,
},
});
Common Use Cases
Removing Items
import React , { useRef , useState } from "react" ;
import { View , Text , Pressable , LayoutAnimation , StyleSheet } from "react-native" ;
import { FlashList } from "@shopify/flash-list" ;
interface TodoItem {
id : string ;
text : string ;
completed : boolean ;
}
const TodoList = () => {
const [ todos , setTodos ] = useState < TodoItem []>([
{ id: "1" , text: "Buy groceries" , completed: false },
{ id: "2" , text: "Walk the dog" , completed: false },
{ id: "3" , text: "Write documentation" , completed: false },
]);
const listRef = useRef < FlashList < TodoItem >>( null );
const removeTodo = ( id : string ) => {
setTodos (( current ) => current . filter (( todo ) => todo . id !== id ));
listRef . current ?. prepareForLayoutAnimationRender ();
LayoutAnimation . configureNext ( LayoutAnimation . Presets . easeInEaseOut );
};
return (
< FlashList
ref = { listRef }
data = { todos }
keyExtractor = { ( item ) => item . id }
renderItem = { ({ item }) => (
< View style = { styles . todoItem } >
< Text style = { styles . todoText } > { item . text } </ Text >
< Pressable
style = { styles . deleteButton }
onPress = { () => removeTodo ( item . id ) }
>
< Text style = { styles . deleteText } > Delete </ Text >
</ Pressable >
</ View >
) }
/>
);
};
const styles = StyleSheet . create ({
todoItem: {
flexDirection: "row" ,
justifyContent: "space-between" ,
alignItems: "center" ,
padding: 16 ,
backgroundColor: "#fff" ,
borderBottomWidth: 1 ,
borderBottomColor: "#e0e0e0" ,
},
todoText: {
fontSize: 16 ,
flex: 1 ,
},
deleteButton: {
backgroundColor: "#ff5252" ,
paddingHorizontal: 16 ,
paddingVertical: 8 ,
borderRadius: 4 ,
},
deleteText: {
color: "#fff" ,
fontWeight: "600" ,
},
});
Adding Items
const addItem = () => {
const newItem = {
id: Date . now (). toString (),
text: "New todo item" ,
completed: false ,
};
setTodos (( current ) => [ newItem , ... current ]);
listRef . current ?. prepareForLayoutAnimationRender ();
LayoutAnimation . configureNext ( LayoutAnimation . Presets . spring );
};
Reordering Items
const moveItemToTop = ( id : string ) => {
setTodos (( current ) => {
const item = current . find (( todo ) => todo . id === id );
if ( ! item ) return current ;
const filtered = current . filter (( todo ) => todo . id !== id );
return [ item , ... filtered ];
});
listRef . current ?. prepareForLayoutAnimationRender ();
LayoutAnimation . configureNext ( LayoutAnimation . Presets . easeInEaseOut );
};
Updating Item Properties
const toggleComplete = ( id : string ) => {
setTodos (( current ) =>
current . map (( todo ) =>
todo . id === id ? { ... todo , completed: ! todo . completed } : todo
)
);
listRef . current ?. prepareForLayoutAnimationRender ();
LayoutAnimation . configureNext ({
duration: 200 ,
update: {
type: LayoutAnimation . Types . easeInEaseOut ,
},
});
};
Animation Presets
easeInEaseOut
spring
linear
Smooth acceleration and deceleration. Good for general-purpose animations. LayoutAnimation . configureNext ( LayoutAnimation . Presets . easeInEaseOut );
Bouncy spring animation. Great for playful interactions. LayoutAnimation . configureNext ( LayoutAnimation . Presets . spring );
Constant speed throughout. Best for simple transitions. LayoutAnimation . configureNext ( LayoutAnimation . Presets . linear );
Custom Animation Configuration
Create sophisticated animations by configuring each phase:
LayoutAnimation . configureNext ({
duration: 400 ,
create: {
type: LayoutAnimation . Types . spring ,
property: LayoutAnimation . Properties . scaleXY ,
springDamping: 0.7 ,
},
update: {
type: LayoutAnimation . Types . easeInEaseOut ,
},
delete: {
type: LayoutAnimation . Types . easeOut ,
property: LayoutAnimation . Properties . opacity ,
},
});
Available Types
LayoutAnimation.Types.spring
LayoutAnimation.Types.linear
LayoutAnimation.Types.easeInEaseOut
LayoutAnimation.Types.easeIn
LayoutAnimation.Types.easeOut
Available Properties
LayoutAnimation.Properties.opacity
LayoutAnimation.Properties.scaleX
LayoutAnimation.Properties.scaleY
LayoutAnimation.Properties.scaleXY
Best Practices
Always use keyExtractor The keyExtractor prop is essential for FlashList to track items correctly during animations.
Call prepare before configure Always call prepareForLayoutAnimationRender() before LayoutAnimation.configureNext().
Test on Android LayoutAnimation is experimental on Android - test thoroughly on actual devices.
Keep animations short Animations between 200-400ms feel responsive without being sluggish.
Troubleshooting
Animations not working at all
Make sure you’ve:
Added a ref to your FlashList
Included the keyExtractor prop
Called prepareForLayoutAnimationRender() before LayoutAnimation.configureNext()
// Enable LayoutAnimation on Android (add to App.tsx)
import { UIManager , Platform } from 'react-native' ;
if ( Platform . OS === 'android' && UIManager . setLayoutAnimationEnabledExperimental ) {
UIManager . setLayoutAnimationEnabledExperimental ( true );
}
Janky or stuttering animations
This usually indicates your renderItem components are doing too much work. Try:
Memoizing your render components
Simplifying component structure
Reducing the number of animated properties
Items jumping or flickering
Ensure your keyExtractor returns stable, unique keys. Keys should not change between renders for the same item.
LayoutAnimation is experimental on Android. If you experience crashes or visual glitches, consider using React Native Reanimated instead.
Alternative: React Native Reanimated
For more complex animations or better Android support, consider using React Native Reanimated:
React Native Reanimated Guide Learn how to use Reanimated with FlashList for more powerful animations
Reanimated Integration Use Reanimated for more advanced animations
Performance Tips Optimize your list for smooth animations