Varför Reanimated 4 förändrar spelplanen
Okej, låt oss prata om vad som faktiskt har hänt med React Native-animationer. Software Mansion har släppt den stabila versionen av React Native Reanimated 4, och ärligt talat — det här är den största uppdateringen sedan worklets dök upp i Reanimated 2. Den stora grejen? CSS-kompatibla animationer och övergångar som körs direkt på UI-tråden med 60+ FPS.
Det är alltså inte bara en liten buggfix-release.
Om du har jobbat med React Native-animationer tidigare vet du hur mycket boilerplate det kan kräva: shared values, useAnimatedStyle, manuella withTiming-anrop och callbacks överallt. Reanimated 4 kastar inte ut den worklet-baserade approachen — den finns kvar och funkar precis som förut — men lägger till ett helt nytt deklarativt lager ovanpå. Tänk dig att du kan skriva animationer med samma syntax som du redan kan från CSS på webben. Ganska najs, eller hur?
I den här guiden går vi igenom allt från de nya CSS-övergångarna och keyframe-animationerna till uppdaterade gest-API:er, layoutanimationer och en steg-för-steg-migrering från version 3. Varje avsnitt har fungerande kodexempel som du kan kopiera rakt in i ditt projekt.
Förutsättningar och installation
Innan vi dyker in, se till att ditt projekt uppfyller grundkraven:
- React Native 0.76 eller nyare (New Architecture / Fabric är ett krav, ingen väg runt det)
- Expo SDK 55 om du kör Expo (New Architecture är standard där)
- react-native-gesture-handler 2.x för gesthantering
Installera Reanimated 4
Kör du Expo? Då är det lätt:
npx expo install react-native-reanimated react-native-gesture-handler
För bara React Native-projekt behöver du installera ett extra paket:
npm install react-native-reanimated react-native-gesture-handler react-native-worklets
Uppdatera Babel-konfigurationen
En viktig förändring som är lätt att missa: worklets har flyttats till det separata paketet react-native-worklets. Det betyder att du behöver uppdatera din babel.config.js:
// babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
// Reanimated 4: byt från 'react-native-reanimated/plugin'
'react-native-worklets/plugin',
],
};
};
Om du uppgraderar från Reanimated 3, byt helt enkelt react-native-reanimated/plugin mot react-native-worklets/plugin. Det gamla pluginet funkar fortfarande (bakåtkompatibilitet), men det är markerat som deprecated så du vill inte bygga nya projekt på det.
CSS-övergångar: det deklarativa sättet att animera
Så, här kommer den stora nyheten. Reanimated 4 stöder CSS-liknande övergångar direkt på Animated-komponenter. Istället för att manuellt skapa shared values och animerade stilar kan du nu deklarera övergångar som stilegenskaper — precis som du gör på webben.
Låter det för bra för att vara sant? Kolla det här exemplet.
Grundläggande CSS-övergång
Här animerar vi bredd och bakgrundsfärg när ett state ändras:
import { useState } from 'react';
import Animated from 'react-native-reanimated';
import { Pressable, StyleSheet, Text } from 'react-native';
export default function CSSTransitionDemo() {
const [expanded, setExpanded] = useState(false);
return (
setExpanded(!expanded)}>
{expanded ? 'Expanderad' : 'Tryck här'}
);
}
Märk hur rent det här är — ingen useSharedValue, ingen useAnimatedStyle, inget withTiming-anrop. Du anger vilka egenskaper som ska animeras, hur lång tid det ska ta, och vilken easing-funktion du vill ha. Reanimated sköter resten på UI-tråden. Första gången jag testade det här blev jag genuint förvånad över hur lite kod som behövdes.
Tillgängliga övergångsegenskaper
De CSS-liknande övergångarna stöder dessa egenskaper:
transitionProperty— en array med egenskapsnamn att animeratransitionDuration— varaktighet i millisekundertransitionTimingFunction— easing-funktion (t.ex.'ease','ease-in-out','linear')transitionDelay— fördröjning innan animationen startar
Det här API:et passar perfekt för enklare UI-animationer styrda av state: knappar som ändrar storlek, kort som expanderar, färger som skiftar. Basically allt som inte kräver gestinput.
CSS-keyframe-animationer
Utöver övergångar har Reanimated 4 också stöd för CSS-keyframe-animationer. Det här ger dig möjlighet att bygga komplexa, sekventiella animationer med samma syntax som @keyframes på webben.
import Animated, { CSSKeyframe } from 'react-native-reanimated';
const pulseAnimation = new CSSKeyframe({
0: { transform: [{ scale: 1 }], opacity: 1 },
50: { transform: [{ scale: 1.15 }], opacity: 0.7 },
100: { transform: [{ scale: 1 }], opacity: 1 },
});
export default function PulseButton() {
return (
);
}
Keyframe-animationer är riktigt bra för laddningsindikatorer, pulserande ikoner, skelettladdningar och andra kontinuerliga animationer. Allt som ska loopa utan användarinteraktion, liksom.
Worklet-baserade animationer: fortfarande kärnan för gester
CSS-animationer är fantastiska för state-drivna övergångar, men för allt som kräver realtidskontroll — gester, scroll-effekter, drag-and-drop — behöver du fortfarande worklets. Det har inte ändrats. Reanimated 4 behåller hela det worklet-baserade API:et från version 3, fast med några välkomna förbättringar.
Shared Values: grunden för allt
En useSharedValue är en animerbar variabel som lever på UI-tråden. Den triggar inga React-renderingar och kan uppdateras med 60+ FPS, vilket gör den idealisk för animationer som måste vara silkeslena:
import { useSharedValue } from 'react-native-reanimated';
// Skapar ett shared value med startvärde 0
const translateX = useSharedValue(0);
// Uppdatera direkt (ingen re-render)
translateX.value = 100;
// Uppdatera med animation
translateX.value = withSpring(100);
useAnimatedStyle: koppla animationer till vyer
Med useAnimatedStyle omvandlar du shared values till stilegenskaper som UI-tråden kan rendera smidigt:
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
export default function AnimatedBox() {
const offset = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: offset.value }],
}));
return (
{
offset.value = withSpring(offset.value === 0 ? 200 : 0);
}}>
);
}
withTiming kontra withSpring
Reanimated har tre inbyggda animationsfunktioner, och att veta vilken du ska välja gör stor skillnad i hur din app känns:
- withTiming — tidbaserad animation med konfigurerbar varaktighet och easing. Perfekt för fade-in/out, slide-in och andra förutsägbara rörelser.
- withSpring — fysikbaserad fjäderanimation. Reagerar på hastighet och överskjutning, vilket ger en naturlig känsla. Min personliga favorit för knapptryckningar och interaktiva element.
- withDecay — retardationsanimation baserad på initial hastighet. Tänk fling-gester och scroll-liknande beteende.
import {
withTiming,
withSpring,
withDecay,
Easing,
} from 'react-native-reanimated';
// Tidbaserad: 500ms med bounce-easing
opacity.value = withTiming(1, {
duration: 500,
easing: Easing.bounce,
});
// Fjäderbaserad: naturlig studseffekt
scale.value = withSpring(1, {
damping: 12,
stiffness: 180,
});
// Retardation: baserad på gestens hastighet
translateX.value = withDecay({
velocity: event.velocityX,
clamp: [-200, 200],
});
Nytt i Reanimated 4: förbättrade fjäderanimationer
Fjäderanimeringens standardvärden har fått en rejäl uppgradering i version 4. Den gamla implementationen kunde vara lite knepig — känslig för start- och slutvillkor, vilket ibland resulterade i ryckiga animationer som inte riktigt kändes bra. Den nya versionen är mer robust och kräver inga tröskelvärden (threshold-parametrar) som standard.
Behöver du av någon anledning behålla det gamla beteendet? Det går att lösa:
import {
withSpring,
Reanimated3DefaultSpringConfig,
} from 'react-native-reanimated';
// Använd gamla standardvärden om du vill matcha v3-beteende
scale.value = withSpring(1, Reanimated3DefaultSpringConfig);
Gestanimationer med Gesture Handler
En av Reanimateds absolut starkaste sidor är den djupa integrationen med React Native Gesture Handler. I version 4 är det det enda sättet att hantera gester — det gamla useAnimatedGestureHandler API:et har tagits bort helt. Och ärligt talat, det nya API:et är bättre på alla sätt.
Pan-gest: dra ett element
Här är ett komplett exempel på en vy som kan dras horisontellt och snäpper tillbaka med en fjäderanimation:
import { StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
export default function DraggableCard() {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const startX = useSharedValue(0);
const startY = useSharedValue(0);
const panGesture = Gesture.Pan()
.onStart(() => {
startX.value = translateX.value;
startY.value = translateY.value;
})
.onUpdate((event) => {
translateX.value = startX.value + event.translationX;
translateY.value = startY.value + event.translationY;
})
.onEnd(() => {
// Snäpp tillbaka till ursprungspositionen
translateX.value = withSpring(0);
translateY.value = withSpring(0);
});
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
}));
return (
{/* Kortinnehåll */}
);
}
const styles = StyleSheet.create({
card: {
width: 200,
height: 120,
backgroundColor: '#8b5cf6',
borderRadius: 16,
justifyContent: 'center',
alignItems: 'center',
},
});
Kombinera gester: pinch-to-zoom
Du kan kombinera flera gester samtidigt med Gesture.Simultaneous(). Här är ett pinch-to-zoom-exempel med rotation som du kan använda direkt i en bildvisare:
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
export default function PinchZoomImage() {
const scale = useSharedValue(1);
const rotation = useSharedValue(0);
const savedScale = useSharedValue(1);
const savedRotation = useSharedValue(0);
const pinchGesture = Gesture.Pinch()
.onUpdate((event) => {
scale.value = savedScale.value * event.scale;
})
.onEnd(() => {
savedScale.value = scale.value;
if (scale.value < 1) {
scale.value = withSpring(1);
savedScale.value = 1;
}
});
const rotationGesture = Gesture.Rotation()
.onUpdate((event) => {
rotation.value = savedRotation.value + event.rotation;
})
.onEnd(() => {
savedRotation.value = rotation.value;
});
const combinedGesture = Gesture.Simultaneous(
pinchGesture,
rotationGesture
);
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ scale: scale.value },
{ rotateZ: `${rotation.value}rad` },
],
}));
return (
);
}
Swipe-to-delete med withDecay
Ett vanligt mönster i appar — svep för att ta bort. Kombinera en pan-gest med withDecay för en naturlig fling-effekt, och en callback som triggas när elementet har försvunnit:
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withDecay,
runOnJS,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
export default function SwipeToDelete({ onDelete }) {
const translateX = useSharedValue(0);
const opacity = useSharedValue(1);
const handleDelete = () => {
onDelete?.();
};
const swipeGesture = Gesture.Pan()
.onUpdate((event) => {
translateX.value = event.translationX;
})
.onEnd((event) => {
if (Math.abs(event.velocityX) > 800 || Math.abs(translateX.value) > 150) {
const direction = translateX.value > 0 ? 1 : -1;
translateX.value = withTiming(direction * 400, { duration: 200 });
opacity.value = withTiming(0, { duration: 200 }, () => {
runOnJS(handleDelete)();
});
} else {
translateX.value = withSpring(0);
}
});
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: translateX.value }],
opacity: opacity.value,
}));
return (
{/* Listinnehåll */}
);
}
Layoutanimationer: enter, exit och övergångar
Reanimated 4 stöder layoutanimationer som automatiskt animerar element när de läggs till i eller tas bort från komponentträdet. Det här är ovärderligt för listor, modaler och villkorligt renderat innehåll — saker som annars bara poppar in och ut utan finesse.
Entering- och exiting-animationer
import Animated, {
FadeInDown,
FadeOutUp,
LinearTransition,
} from 'react-native-reanimated';
export default function AnimatedList({ items }) {
return (
(
{item.title}
)}
/>
);
}
Reanimated kommer med en hel radda inbyggda animationer:
- Entering:
FadeIn,FadeInDown,FadeInUp,SlideInLeft,SlideInRight,ZoomIn,BounceIn - Exiting:
FadeOut,FadeOutDown,FadeOutUp,SlideOutLeft,SlideOutRight,ZoomOut - Layout:
LinearTransition,SequencedTransition,FadingTransition
Du kan kedja modifierare som .duration(), .delay(), .springify() och .damping() för att finjustera beteendet. Experimentera med olika kombinationer — det är det enklaste sättet att hitta rätt känsla.
Kombinera CSS-övergångar och worklets
En av de smartaste sakerna med Reanimated 4 är att CSS-övergångar och worklet-baserade animationer kan leva sida vid sida i samma komponent. Det ger dig det bästa av två världar, och det funkar förvånansvärt smidigt:
import { useState } from 'react';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
export default function HybridCard() {
const [isActive, setIsActive] = useState(false);
const translateY = useSharedValue(0);
const startY = useSharedValue(0);
const panGesture = Gesture.Pan()
.onStart(() => {
startY.value = translateY.value;
})
.onUpdate((event) => {
translateY.value = startY.value + event.translationY;
})
.onEnd(() => {
translateY.value = withSpring(0);
});
const dragStyle = useAnimatedStyle(() => ({
transform: [{ translateY: translateY.value }],
}));
return (
setIsActive(!isActive)}
style={[
{
width: 250,
height: isActive ? 200 : 100,
backgroundColor: isActive ? '#3b82f6' : '#93c5fd',
borderRadius: 16,
// CSS-övergångar för state-ändringar
transitionProperty: ['height', 'backgroundColor'],
transitionDuration: 300,
transitionTimingFunction: 'ease-out',
},
// Worklet-animation för drag
dragStyle,
]}
/>
);
}
I det här exemplet hanteras höjd- och färgförändringen av CSS-övergångar (deklarativt, state-drivet), medan drag-rörelsen hanteras av worklets (imperativt, gest-drivet). Två system, noll konflikter.
Migrering från Reanimated 3 till 4
Använder du redan Reanimated 3? Uppgraderingen är faktiskt ganska smärtfri i de flesta fall. Här är en checklista med det viktigaste du behöver kolla av.
1. Kräver New Architecture
Reanimated 4 fungerar enbart med New Architecture (Fabric). Kör din app fortfarande på den gamla arkitekturen? Då behöver du stanna kvar på Reanimated 3 tills du har migrerat. Med Expo SDK 55 är New Architecture standard, så de flesta Expo-projekt borde redan vara redo.
2. Uppdatera Babel-plugin
Byt react-native-reanimated/plugin till react-native-worklets/plugin i din babel.config.js. Snabbt och enkelt.
3. Ersätt borttagna API:er
Följande API:er har tagits bort i version 4:
// BORTTAGET: useAnimatedGestureHandler
// Ersätt med Gesture API från react-native-gesture-handler 2.x
// Före (Reanimated 3):
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => { ctx.startX = translateX.value; },
onActive: (event, ctx) => {
translateX.value = ctx.startX + event.translationX;
},
onEnd: () => { translateX.value = withSpring(0); },
});
// Efter (Reanimated 4):
const panGesture = Gesture.Pan()
.onStart(() => { startX.value = translateX.value; })
.onUpdate((event) => {
translateX.value = startX.value + event.translationX;
})
.onEnd(() => { translateX.value = withSpring(0); });
// BORTTAGET: combineTransition
// Ersätt med EntryExitTransition.entering().exiting()
// BORTTAGET: useWorkletCallback
// Ersätt med useCallback och 'worklet'-direktivet
4. Uppdatera worklet-importer (rekommenderat)
Worklet-funktioner re-exporteras fortfarande från react-native-reanimated för bakåtkompatibilitet, men du bör byta till de nya importerna för framtidssäkerhet:
// Före (deprecated men fungerar fortfarande):
import { runOnJS, runOnUI } from 'react-native-reanimated';
// Rekommenderat:
import { scheduleOnRN, scheduleOnUI } from 'react-native-worklets';
// runOnJS(fn)(args) blir:
scheduleOnRN(fn, args);
// runOnUI(fn)(args) blir:
scheduleOnUI(fn, args);
5. Uppdatera tredjepartsbibliotek
Kolla att dina beroenden hänger med:
@gorhom/bottom-sheet— uppgradera till version 5.1.8 eller nyarereact-native-gesture-handler— version 2.x krävs- Se till att
react-native-workletsinte installeras bredvid Reanimated 3 — de är inkompatibla och det blir kaos
Prestandatips för produktionsappar
Reanimated 4 ger imponerande prestanda som standard, men det finns några saker att ha i åtanke för att hålla animationerna smidiga i produktion:
- Undvik att animera layout-egenskaper som
widthochheighti listor med många element. Föredratransform(scale,translateX/Y) ochopacitysom kan köras helt på GPU:n. - Använd
cancelAnimationi cleanup-funktioner för att undvika minnesläckor när komponenter avmonteras. - Begränsa antalet samtidiga animationer — framförallt på äldre Android-enheter där det märks tydligt.
- Välj CSS-övergångar framför worklets för state-drivna animationer. De är enklare att underhålla och Reanimated optimerar dem internt.
- Använd
useDerivedValueistället för att beräkna värden inutiuseAnimatedStyle. Det gör koden mer läsbar och kan minska onödiga beräkningar.
CSS-övergångar kontra worklets: när ska du använda vad?
Med två parallella animationssystem kan det vara lite förvirrande att veta vilken approach du ska välja. Här är en enkel tumregel som har fungerat bra för mig.
Använd CSS-övergångar för
- Animationer som triggas av state-ändringar (öppna/stäng, aktiv/inaktiv)
- Färgändringar, storleksändringar, opacity-övergångar
- Laddningsanimationer och skelettplatshållare
- Hover- och fokus-effekter (särskilt på webben med React Native Web)
Använd worklets för
- Gestdrivna animationer (drag, swipe, pinch)
- Scroll-beroende effekter (parallax, sticky headers)
- Animationer som behöver realtidskontroll av varje frame
- Komplexa sekvenser med
withSequence,withRepeatoch callbacks
Vanliga frågor
Fungerar Reanimated 4 med Expo?
Absolut. Reanimated 4 fungerar utmärkt med Expo SDK 55 och senare. Eftersom New Architecture är standard i SDK 55 behöver du inte göra några extra konfigurationsändringar utöver att installera paketet och uppdatera Babel-konfigurationen. Kör npx expo install react-native-reanimated så får du rätt version automatiskt.
Kan jag använda Reanimated 4 utan New Architecture?
Tyvärr inte. Reanimated 4 kräver React Natives New Architecture (Fabric). Kör din app fortfarande på den gamla arkitekturen? Stanna på Reanimated 3 som fortfarande underhålls. Eller ännu bättre — migrera till New Architecture. Med Expo SDK 55 är det en rätt smidig upplevelse.
Vad har hänt med useAnimatedGestureHandler?
useAnimatedGestureHandler togs bort i Reanimated 4 efter att ha varit deprecated sedan version 3. Ersätt det med Gesture API:et från react-native-gesture-handler version 2. Det nya API:et är mer flexibelt, enklare att kombinera, och har bättre TypeScript-stöd. Ingen anledning att sakna det gamla, om jag ska vara ärlig.
Är CSS-animationer i Reanimated lika snabba som worklets?
Ja! CSS-övergångar och keyframe-animationer i Reanimated 4 körs på UI-tråden med samma prestanda som worklet-baserade animationer. Skillnaden handlar om API-design — CSS-varianten är deklarativ och kräver mindre kod, medan worklets ger mer finkorning kontroll. Båda levererar 60+ FPS.
Hur skiljer sig Reanimated 4 från Moti?
Moti är ett deklarativt animationsbibliotek byggt ovanpå Reanimated. Med Reanimated 4:s nya CSS-animationer löser man en del av Motis tidigare användningsfall — enklare deklarativa animationer kan nu göras direkt med Reanimated utan extra beroenden. Moti kan dock fortfarande vara värdefullt för sin specifika API-design (inspirerad av Framer Motion) och fungerar även på webben via React Native Web.