React Native Reanimated 4: Guide till CSS-animationer, gester och layoutövergångar

Lär dig använda React Native Reanimated 4 med CSS-övergångar, keyframe-animationer, pan- och pinch-gester samt layoutanimationer. Komplett migreringsguide från version 3 med kodexempel.

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 animera
  • transitionDuration — varaktighet i millisekunder
  • transitionTimingFunction — 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 nyare
  • react-native-gesture-handler — version 2.x krävs
  • Se till att react-native-worklets inte 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 width och height i listor med många element. Föredra transform (scale, translateX/Y) och opacity som kan köras helt på GPU:n.
  • Använd cancelAnimation i 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 useDerivedValue istället för att beräkna värden inuti useAnimatedStyle. 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, withRepeat och 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.

Om Författaren Editorial Team

Our team of expert writers and editors.