Automatic transitions with zero manual timing
Reanimated Layout Animations automatically compute smooth transitions the moment a component's size, position, or visibility changes, without developers manually orchestrating start and end values through useState or useEffect. This article covers entering, exiting, and layout transitions using a list with add and remove animations as an example, plus the performance limits for very complex lists.
Table of Contents
- 1. The problem with manually orchestrated transitions
- 2. Entering animations: how new elements appear
- 3. Exiting animations: how elements disappear
- 4. Layout transitions: automatic transitions for position changes
- 5. Practical example: a list with add and remove animations
- 6. Custom transitions instead of predefined presets
- 7. Performance limits for complex lists
- 8. Debugging layout animations and common failure patterns
- 9. Combining layout animations with gesture-driven changes
- 10. Summary
- 11. FAQ
1. The problem with manually orchestrated transitions
Without layout animations, every transition, say removing a list item, has to be choreographed manually: first animate opacity to zero, then actually remove the item from state once the animation finishes, and at the same time make sure the remaining items smoothly shift upward instead of jumping abruptly. Coordinating this across several useState and useEffect calls is error-prone and gets more complex with every additional animation.
Reanimated Layout Animations solve exactly this problem by automatically detecting and interpolating size and position changes of a component, without the developer having to manually calculate intermediate steps. The moment a component is removed from the tree, newly inserted, or moved to a new position, Reanimated takes over animating that transition on its own.
2. Entering animations: how new elements appear
The entering prop on an Animated.View lets you define how a component enters the visible tree the first time it renders. Predefined presets such as FadeIn, SlideInRight, or ZoomIn cover most use cases and can be fine-tuned through modifier methods like .duration() or .delay().
It matters that the entering animation fires automatically on every mount of the component, not just on the app's first render. If an element gets shown again through conditional rendering, say after a filter change in a list, Reanimated plays the entering animation again, with zero extra code.
import Animated, { FadeIn, SlideInRight } from 'react-native-reanimated';
function NewMessageBubble({ text }: { text: string }) {
return (
<Animated.View
entering={SlideInRight.duration(300).easing(Easing.out(Easing.cubic))}
>
<Text>{text}</Text>
</Animated.View>
);
}
3. Exiting animations: how elements disappear
The counterpart is the exiting prop, which defines an animation for the moment a component is removed from the tree, say FadeOut or SlideOutLeft. Reanimated keeps the component internally in the native view tree until the animation finishes, then removes it automatically, without the developer maintaining a timeout or extra state.
That is the key difference from a manual solution: from React's point of view, the component is gone the instant it disappears from the parent tree, while Reanimated, at the UI-thread level, delays the actual removal until the exiting animation completes.
import Animated, { FadeOut, Layout } from 'react-native-reanimated';
function TodoItem({ item, onRemove }: { item: Todo; onRemove: () => void }) {
return (
<Animated.View exiting={FadeOut.duration(250)} layout={Layout.springify()}>
<TodoRow item={item} onPress={onRemove} />
</Animated.View>
);
}
4. Layout transitions: automatic transitions for position changes
The layout prop, usually configured with LinearTransition or Layout.springify(), makes a component automatically animate from its old to its new position and size the moment the layout changes, say because a sibling element was removed or the component's own size changes due to new text content.
Reanimated internally compares the layout before and after a React update and automatically calculates the necessary transform, without start and target values needing to be measured manually. Combined with entering and exiting, this creates a complete, consistent transition system for dynamic lists.
5. Practical example: a list with add and remove animations
The following example combines all three building blocks in a simple todo list: new entries slide in from the right through entering, removed entries fade out through exiting, and all remaining entries automatically shift to their new position through layout, without the list itself knowing any animation logic at all.
It matters that every list item has a stable key, usually the entry's ID, because Reanimated uses that key to determine whether a component is freshly mounted, continues to exist unchanged, or was removed. An unstable key, say the array index, leads to animations being applied to the wrong elements.
function TodoList({ items }: { items: Todo[] }) {
return (
<View>
{items.map((item) => (
<Animated.View
key={item.id}
entering={SlideInRight}
exiting={FadeOut}
layout={LinearTransition.springify().damping(18)}
>
<TodoRow item={item} />
</Animated.View>
))}
</View>
);
}
6. Custom transitions instead of predefined presets
For cases where the predefined presets fall short, custom entering and exiting animations can be defined as a worklet that returns start and end values for arbitrary style properties. That opens up the ability to animate several properties at once or match an animation to a specific design language not covered by the shipped presets.
These custom animations run just as fully on the UI thread as the predefined presets do, since they too ultimately get compiled as worklets, which means the performance characteristics and pitfalls of the worklet model carry over directly.
const CustomEntering = (targetValues: LayoutAnimationsValues) => {
'worklet';
const animations = {
opacity: withTiming(1, { duration: 300 }),
transform: [{ scale: withSpring(1) }],
};
const initialValues = { opacity: 0, transform: [{ scale: 0.8 }] };
return { initialValues, animations };
};
7. Performance limits for complex lists
For short to medium lists rendered entirely as regular React components, layout animations work with virtually no limitations. For very long lists rendered through a virtualized FlatList, entering and exiting hit a limit: virtualized lists constantly mount and unmount elements outside the visible area, which makes Reanimated attempt to animate elements the user never actually sees.
In practice, that means applying layout animations deliberately, only for visible, short-lived changes, say adding or removing an entry through a direct user action, instead of applying them blanket-style to every item of a very long, virtualized list. With several hundred simultaneously animated elements, the number of parallel UI-thread calculations can also become noticeable even on powerful devices.
8. Debugging layout animations and common failure patterns
A common problem is that a layout transition simply does not show up because the affected component is not directly an Animated.View, but wrapped in a plain View that itself receives no layout prop. Reanimated can only animate layout changes for components that are themselves rendered as an animated component, not for arbitrary nested child elements.
A second widespread issue involves the New Architecture: layout animations build on Fabric and can behave inconsistently on older, not-yet-migrated parts of an app. Testing on both platforms with the New Architecture enabled is therefore advisable before shipping layout animations in critical lists.
9. Combining layout animations with gesture-driven changes
Layout animations combine well with gesture-driven interactions, say when a list item removed through a swipe gesture is finally hidden through exiting once the swipe completes, while the remaining items automatically shift through layout. The gesture itself only controls visibility and state, the actual transition animation of the list stays entirely with Reanimated.
This combination of manually driven gesture animations and automatic layout animations is a good example of how the two concepts complement rather than replace each other: gestures for direct, physically plausible interaction, layout animations for the structural consequence of that interaction.
| Situation | Recommended prop | Typical preset | Note |
|---|---|---|---|
| A new element appears | entering |
FadeIn, SlideInRight |
Fires on every mount, not just app start |
| An element gets removed | exiting |
FadeOut, SlideOutLeft |
Component stays in the native tree until the animation ends |
| Sibling elements shift | layout |
LinearTransition, Layout.springify() |
Needs a stable key per element |
| Very long, virtualized list | Targeted, not blanket | No preset on every item | FlatList mounts/unmounts outside the viewport constantly |
| Custom, non-preset animation | Custom worklet | own initialValues/animations | Runs entirely on the UI thread |
| Combination with a swipe gesture | exiting after gesture end |
depends on the use case | Gesture drives state, layout animation drives the transition |
Mironsoft
React Native app development and Magento integration
A mobile app for the Magento shop that actually runs smoothly?
We build React Native apps cleanly connected to the Magento REST or GraphQL API, from the first line of code to publishing on the App Store and Google Play.
App Concept
Plan the architecture and feature scope of a Magento-connected app together.
Magento API Integration
Cleanly connect product catalog, cart, and checkout to the shop API.
Store Publishing
Guide the App Store and Google Play release process without pitfalls.
10. Summary
Layout Animations
Core idea
Reanimated automatically computes transitions on mount, unmount, and position change, with zero manual timing.
Three building blocks
entering for new elements, exiting for removed elements, layout for position changes of existing elements.
Key rule
Every list item needs a stable key, otherwise animations get applied to the wrong elements.
Performance limit
Apply deliberately, not blanket-style, in very long virtualized lists, since FlatList constantly mounts and unmounts.