Navigation jank is the first thing users notice and the last thing developers test. When a destination screen starts rendering a 500-row FlatList or firing a data fetch during the transition animation, the JS thread is saturated and the animation drops below 60 FPS. ISO 25010:2011 performance-efficiency covers exactly this: degraded responsiveness under normal operating conditions. Users experiencing jank in the first 300ms of a screen transition — when the UI is most visually exposed — rate apps lower in store reviews and uninstall at higher rates than users who experience equivalent latency hidden behind a loading state.
Low because jank degrades perceived quality and store ratings but does not block functionality or expose data; it is an experience failure, not a functional or security failure.
Defer heavy rendering until after the navigation animation completes using InteractionManager.runAfterInteractions or a short useFocusEffect delay:
import { InteractionManager } from 'react-native'
import { useFocusEffect } from '@react-navigation/native'
function HeavyListScreen() {
const [ready, setReady] = React.useState(false)
useFocusEffect(
React.useCallback(() => {
const task = InteractionManager.runAfterInteractions(() => {
setReady(true)
})
return () => task.cancel()
}, [])
)
if (!ready) return <SkeletonPlaceholder />
return <FlatList data={data} renderItem={renderItem} />
}
InteractionManager.runAfterInteractions waits for all in-flight animations to complete before firing — more precise than a fixed timeout. Use a skeleton placeholder during the wait so the transition visually lands on real content, not a blank screen.
ID: mobile-navigation-linking.navigation-ux.smooth-transitions
Severity: low
What to look for: Test navigation by rapidly transitioning between screens. Observe for frame drops, stuttering, or lag during screen transitions. Check for expensive operations (large list rendering, data fetching) happening during navigation. Examine screen options for animation configuration.
Pass criteria: Count all screens that render large lists or perform heavy operations. Screen transitions must target at least 60 FPS. No visible stuttering or jank during navigation. At least 1 performance optimization pattern (InteractionManager, lazy loading, or deferred rendering) must exist for screens with heavy content.
Fail criteria: Navigation transitions are visibly janky or stutter. Frame drops occur during screen transitions.
Skip (N/A) when: The app has fewer than 5 screens and no heavy rendering requirements.
Detail on fail: "Screen transitions are visibly stuttering, especially with lists on the destination screen" or "Navigation is blocked when transitioning to a screen that renders a large list"
Remediation: Optimize navigation animations by deferring heavy operations:
function HeavyScreen() {
const [isReady, setIsReady] = useState(false)
const [data, setData] = useState([])
useFocusEffect(
useCallback(() => {
// Defer heavy rendering until after navigation completes
const timer = setTimeout(() => {
fetchAndRenderData().then(setData)
setIsReady(true)
}, 100) // Small delay for animation to complete
return () => clearTimeout(timer)
}, [])
)
return isReady ? <HeavyList data={data} /> : <LoadingPlaceholder />
}
Also, consider using screenOptions to customize or disable animations if needed:
<Stack.Navigator screenOptions={{ animationEnabled: true }}>
{/* animations enabled by default */}
</Stack.Navigator>