Grab 30% off Pro with

LAUNCH

ends in

Buy now

Performance

ExpoKit targets 60fps touch interactions without mandatory Reanimated. The patterns below are what the copied components already follow — keep them when you edit the source.

Use the native driver

Springs and fades in the kit use Animated with useNativeDriver: true for transform and opacity. That keeps animation off the JavaScript thread while React is busy.

tsx
import { Animated } from "react-native";import { motion } from "@/lib/kit"; Animated.spring(scale, {  toValue: pressed ? 0.96 : 1,  useNativeDriver: true,  ...motion.bouncy,}).start();
Avoid animating width, height or margin every frame — they force layout. Prefer transform and opacity, or a one-time layout measure then animate scale.

Do not setState every frame

Driving UI from scroll or drag with setState re-renders the whole subtree on every event. Keep the animated value in Animated.Value (or Reanimated shared values if you adopt them). Only commit to React state when the gesture ends or when a human-readable label must change.

Lists

Long settings screens should use FlatList or FlashList, not a ScrollView of hundreds of rows. Memoize row components and stabilise renderItem with useCallback.

Measure in release

Debug builds are slower. Profile jank on a real device in release before ripping out animations:

terminal
npx expo run:ios --device --configuration Release# ornpx expo run:android --variant release

Reduced motion

Where the kit loops (skeleton shimmer, loading pulses), respect the system accessibility setting when you add your own loops — static fallbacks are both correct and cheaper.