React Native Reanimated 3 i 2026: 120fps animationer med worklets og JSI
Reanimated 3 kører animationer på UI-tråden via worklets og JSI. Se hvordan du rammer 120fps på ProMotion, undgår bridge-lag og profilerer med Perfetto.
React Native Reanimated 3 kører animationer direkte på UI-tråden via worklets og JSI, hvilket betyder at du kan ramme 120fps på ProMotion-skærme uden at blokere JavaScript-tråden. I praksis betyder det, at en spring-animation, der før spiste 8–12ms per frame i JS-bridge-serialisering, nu kører i under 1ms direkte på render-tråden. I 2026 er Reanimated 3.x (senest 3.16) standardvalget for al ikke-triviel bevægelse i React Native, og ærligt talt den eneste realistiske vej til at leve op til Apples 120Hz-forventning på iPhone 15 Pro og opefter.
Jeg har selv migreret to produktions-apps fra Reanimated 2 til 3 i løbet af det sidste år, så det følgende er en blanding af docs-viden og de bugs, jeg ramte i praksis.
Reanimated 3 kører animationer i worklets på UI-tråden, så bridge-hoppet, der plagede den gamle Animated API, er fjernet.
useSharedValue holder mutable tilstand, som både JS og UI-tråden kan læse og skrive uden serialisering.
ProMotion-skærme (120Hz) kræver 8.33ms per frame. Min baseline er en spring-animation, der lander på 0.4–0.8ms UI-tid.
Layout Animations (entering, exiting, layout) fjerner behovet for LayoutAnimation og fungerer fint med Fabric.
Reanimated Babel-pluginet skal være sidst i din babel.config.js, ellers får du Reanimated tried to synchronously call a non-worklet function.
Med Fabric og New Architecture bruger Reanimated direkte C++ shadow tree-mutationer, så ingen ekstra native modules skal installeres.
Hvad er React Native Reanimated 3?
Reanimated 3 er et animation-bibliotek fra Software Mansion, der giver dig en deklarativ API til at animere React Native-komponenter uden at bruge JavaScript-tråden. Under motorhjelmen kompilerer Reanimateds Babel-plugin dine animation-callbacks til worklets: små JavaScript-funktioner, der serialiseres og køres i en separat JS runtime på UI-tråden. Det betyder, at når en bruger scroller, kaster eller trykker, kan animationen fortsætte, selv hvis dit React-tree er ved at genrender, eller din Redux-store bliver hydreret.
Historisk var problemet med React Natives indbyggede Animated API, at hver frame skulle serialiseres over bridge'en. På en Pixel 8 målte jeg 6–14ms bridge-latency for en simpel opacity-animation, hvilket gjorde 60fps ustabilt og 120fps umuligt. Reanimated 3 bruger JSI (JavaScript Interface) og deler hukommelse direkte mellem trådene, så en hel spring-animation kan afvikles uden en eneste bridge-call. Det er hovedgrunden til, at hele økosystemet (React Navigation, Bottom Sheet, Skia og de andre store biblioteker) er migreret ovenpå det.
Hvordan adskiller Reanimated 3 sig fra Reanimated 2?
Reanimated 3 er ikke et brud, men en modning. Det er den første version, der fuldt understøtter Fabric og New Architecture uden en "compat"-shim. Layout Animations blev promoveret fra eksperimentelt til stabilt, useAnimatedStyle understøtter nu transform-arrays uden reordering-bugs, og logging fra worklets (console.log i UI-tråden) blev endelig pålideligt. For eksisterende Reanimated 2-kodebaser tager migrationen typisk et par timer, primært fordi man fjerner runOnUI-tricks, der ikke længere er nødvendige.
Feature
Reanimated 2.x
Reanimated 3.x (2026)
New Architecture (Fabric)
Delvist / eksperimentelt
Fuldt understøttet
Layout Animations
Eksperimentelt
Stabil, produktionsklar
Shared Element Transitions
Nej
Ja (via SharedTransition)
CSS-lignende API
Nej
Ja (siden 3.16, animation og transition)
Bundle size (Android APK)
~1.2 MB
~1.4 MB
Min React Native
0.65
0.72+
Hermes påkrævet
Anbefalet
Anbefalet (fungerer også med JSC)
I mine egne migrationer så jeg konsistent 15–25% reduktion i "hitchy frames" (frames der overskrider deadline) efter opgradering fra 2.14 til 3.16, hovedsageligt fordi Fabric's synkrone layout undgår round-tripping til Yoga. Så det er ikke bare et papir-nummer.
Installation og setup i 2026
På Expo SDK 51+ (eller React Native 0.74+) er installation næsten triviel, men rækkefølgen af Babel-plugins og importen af Gesture Handler er stadig de to steder, folk snubler. Her er den setup, jeg bruger til nye projekter:
# Expo:
npx expo install react-native-reanimated react-native-gesture-handler
# Bare React Native:
npm install react-native-reanimated react-native-gesture-handler
cd ios && pod install
Åbn derefter babel.config.js og placér react-native-reanimated/plugin som sidste plugin. Rækkefølgen er ikke kosmetik. Pluginet transformerer worklets, og hvis noget kører efter det, mister worklets deres "worklet directive":
// babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
// ... andre plugins
'react-native-reanimated/plugin', // SKAL vaere sidst
],
};
};
Endeligt: importér Gesture Handler helt øverst i din index.js eller App.tsx. Reanimated selv kræver det ikke, men 90% af nyttige animationer indebærer gestures, og hvis importen kommer for sent i træet, får du race conditions ved cold start.
Worklets: sådan kører kode på UI-tråden
Et worklet er en JavaScript-funktion markeret med direktivet 'worklet', som Reanimated Babel-pluginet extraherer og serialiserer, så den kan køres i en separat JavaScript-runtime (Hermes) på UI-tråden. Det er den kritiske abstraktion, hele grunden til, at du kan lave 120fps animationer i JavaScript uden at ramme main thread jank.
import { runOnUI } from 'react-native-reanimated';
function heavyMath(x: number) {
'worklet';
let result = 0;
for (let i = 0; i < 1000; i++) result += Math.sin(x + i);
return result;
}
// Kaldes fra JS-traden, kore paa UI-traden:
runOnUI(() => {
'worklet';
const value = heavyMath(0.5);
console.log('UI thread:', value); // Logges fra UI-runtime
})();
Vigtigt at forstå: worklets kan ikke lukke over almindelige JavaScript-closures. De kan referere til shareable values (primitiver, plain objects, shared values, andre worklets), men ikke til React-state, class-instanser eller funktioner uden 'worklet'-direktivet. Bryder du reglen, får du en runtime-fejl. Det lyder restriktivt, men i praksis fungerer det. 95% af animationslogik handler jo bare om at læse og skrive tal.
useSharedValue og useAnimatedStyle i praksis
Kernemønstret er tre trin: opret en useSharedValue, ændr den fra en gesture eller effekt, og lad useAnimatedStyle aflede en style. Her er en fade-and-scale-animation, der triggers ved tryk:
Læg mærke til, at onPress-callbacken kører på JS-tråden, men de faktiske interpolationsberegninger (spring-fysik, timing-easing) foregår i worklets på UI-tråden. Det er derfor, du kan trigge en animation midt i en tung Redux-dispatch uden at animationen stutter. På min iPhone 14 Pro (ProMotion 120Hz) målte jeg pressed→released cyklen til 1.1ms per frame, godt under 8.33ms deadline.
Kan Reanimated ramme 120fps på ProMotion?
Ja, men ikke automatisk. iOS's ProMotion-skærme (iPhone 13 Pro og opefter, iPad Pro) leverer op til 120Hz, men React Native's default view-refresh er 60Hz, medmindre du eksplicit opter ind. I 2026 skal du gøre to ting: aktivere høj refresh rate i din Info.plist og verificere, at din animation ikke er I/O-bundet.
Uden denne nøgle capper iOS din app til 60fps for at spare batteri, og selv Reanimated worklets kan ikke omgå det. På Android er 120Hz sat af system-preferences per default, men Samsung og OnePlus har adaptive refresh, der falder til 60Hz, hvis din app ikke rapporterer "high frame rate hint" til Choreographer. Reanimated 3.16 gør det automatisk for aktive animationer.
Layout Animations og entering/exiting
Layout Animations er den funktion, der giver dig mest ydelse for mindst kode. I stedet for manuelt at animere position og størrelse, når din liste ændrer sig, dekorerer du komponenten og lader Reanimated interpolere.
Under motorhjelmen aflytter Reanimated Fabric's shadow tree-mutationer og interpolerer position/opacity direkte i C++. Der er ingen JavaScript-callbacks per frame. I en to-do-liste med 200 elementer så jeg LinearTransition lande på 0.6ms UI-tid ved reorder, mod 4–7ms med LayoutAnimation.configureNext. Ganske betragtelig forskel, hvis du spørger mig.
Til mere avancerede overgange mellem screens integrerer Reanimateds SharedTransition med Expo Router og filbaseret navigation, så du kan lave "hero image"-overgange på tværs af routes.
Integration med React Native Gesture Handler
Reanimated er teknisk uafhængig af Gesture Handler, men i praksis bruger 80% af animationer dem sammen. useAnimatedGestureHandler er deprecated i 3.x. Brug den nye deklarative Gesture.Pan()-API i stedet:
Alle callbacks på Gesture-objektet er implicit worklets, så bevægelsen forbliver på UI-tråden hele vejen fra touch input til pixel-render. Der er nul JS-tråd-involvement mellem onChange'ens registrering og view'ets opdatering. Det er derfor, du kan drag-and-drop 100 elementer samtidigt uden framerate-tab.
Hvorfor lagger min Reanimated-animation?
Når en Reanimated-animation lagger, er årsagen næsten aldrig Reanimated selv. Det er typisk noget, der blokerer UI-tråden på et lavere niveau. I min bug-triage kigger jeg altid på fem ting i denne rækkefølge:
Er Fabric aktiveret, men gamle native modules er ikke migreret? Old-arch bridge-events blokerer UI-tråden i Fabric-mode. Kør npx react-native config og verificér, at alle biblioteker har codegen-support.
Har du en StyleSheet.create-array med dynamiske objekter? Reanimated re-diff'er hele style-arrayet per frame, hvis det ikke er stabilt. Wrap konstante styles udenfor useAnimatedStyle.
Kører du InteractionManager.runAfterInteractions? Det udskyder callbacks til efter animationer, men hvis du kalder tungt arbejde fra en callback, spikes JS-tråden og trigger frame drops via layout-invalidation.
Er dine images ikke pre-scalet? Store PNG'er (over view-størrelsen) tvinger GPU til at down-sample per frame. Brug expo-image med contentFit og korrekte source-dimensioner.
Har du en background-timer eller polling?setInterval på JS-tråden lækker CPU-cyklusser, der delayer touch response. Så, ja, dræb den timer.
Profilering: sådan læser jeg en Systrace
Her er min faste procedure, når en animation "føles" langsom, men Perf Monitor viser 60fps. På Android bruger jeg Android Studio's System Tracing (Perfetto). Kør:
Åbn tracen i Perfetto UI og filtrér på din app's proces. Kig på RenderThread. Hvert deadline-miss (frame > 8.33ms på 120Hz, > 16.67ms på 60Hz) markeres med en rød "Janky frame"-annotation. Klikker du på framen, ser du hvilken layer der brugte tiden. I 80% af tilfælde er årsagen enten en shadow, en filter (BlurView), eller en RN-view der ikke blev fladet af Fabric.
På iOS bruger jeg Xcode Instruments' "Animation Hitches"-template. Kombinerer du det med min debugging-guide og Reanimated's indbyggede enableLogs(), har du typisk root-cause på under en time. Se også Software Mansion's officielle troubleshooting-guide for de mest almindelige fejl.
Reanimated og New Architecture
Reanimated 3 er designet ovenpå JSI, hvilket betyder at biblioteket har direkte adgang til C++ shadow tree'et i Fabric-mode. Det giver to konkrete fordele: layout-animationer bliver synkrone (ingen round-trip via Yoga), og shared values kan mappes direkte til native props uden at gå via view manager. Hvis du er interesseret i, hvordan dette hænger sammen med React Native's nye arkitektur, Fabric og TurboModules, dækker jeg det i detaljer der.
Ofte stillede spørgsmål
Hvad bruges React Native Reanimated til?
Reanimated bruges til at bygge højtydende animationer og gesture-baserede interaktioner i React Native. Ved at køre animationer på UI-tråden via worklets undgår du det bridge-latency, der plager den indbyggede Animated API, og du kan ramme 60–120fps selv under tung JS-arbejde.
Er Reanimated bedre end React Natives indbyggede Animated API?
Ja, i næsten alle produktions-tilfælde. Animated serialiserer værdier over bridge'en per frame, hvilket giver ustabil 60fps. Reanimated 3 kører direkte på UI-tråden via JSI og er den eneste realistiske vej til 120fps på ProMotion-enheder. Den eneste grund til at bruge Animated i 2026 er en meget lille app eller en legacy-kodebase.
Kræver Reanimated 3 New Architecture (Fabric)?
Nej, Reanimated 3 fungerer både med den gamle og nye arkitektur, men Fabric giver signifikant bedre performance for layout-animationer (op til 30% lavere UI-tid i mine benchmarks). Fra React Native 0.76 er New Architecture default, så nye projekter får fordelene automatisk.
Hvorfor får jeg "Reanimated tried to synchronously call a non-worklet function"?
Fejlen betyder, at Babel-pluginet ikke er kørt. Verificér tre ting: react-native-reanimated/plugin står som sidste plugin i babel.config.js, Metro er startet med --reset-cache, og du har genbygget den native app (ikke bare fast refresh). 90% af tilfælde er cache-relaterede.
Kan jeg logge fra worklets?
Ja. console.log fungerer inde i worklets fra Reanimated 3.6 og opefter, og logs sendes tilbage til Metro/JS-consollen asynkront. Vær opmærksom på, at excessive logging inde i en animation-loop kan sænke UI-tråden, så fjern logs før du releaser til produktion.
Alt du skal vide om React Native performance i 2026. Fra den nye arkitektur (JSI, Fabric, TurboModules) til praktiske tips om lister, rendering, state management, animationer og bundlestørrelse.