React Native Reanimated 4: Animazioni CSS, Gesture e Shared Transitions

Guida pratica a React Native Reanimated 4: animazioni CSS dichiarative, worklet, shared values, integrazione con Gesture Handler e le nuove Shared Element Transitions. Esempi di codice pronti all'uso per UI fluide a 60-120 fps.

Perché Reanimated 4 Cambia le Regole del Gioco

Se hai mai provato a creare animazioni davvero fluide in React Native, sai quanto può essere frustrante. Frame drop, stuttering durante lo scroll, gesti che rispondono con mezzo secondo di ritardo — sono problemi che rovinano l'esperienza utente e, onestamente, fanno sembrare la tua app una web app incollata su un telefono.

React Native Reanimated 4 affronta questi problemi alla radice.

Rilasciato nella versione stabile a luglio 2025 e aggiornato alla 4.2.2 a febbraio 2026, Reanimated 4 porta con sé un paradigma completamente nuovo: le animazioni CSS dichiarative accanto ai tradizionali worklet, il tutto eseguito direttamente sul thread UI nativo. Il risultato? Animazioni a 60-120 fps senza mai bloccare il thread JavaScript. Non è poco.

In questa guida vediamo tutto quello che ti serve per padroneggiare Reanimated 4: dall'installazione ai concetti fondamentali, dalle animazioni CSS alle gesture, fino alle Shared Element Transitions introdotte nella versione 4.2.0. Ogni sezione include esempi di codice funzionanti che puoi copiare direttamente nel tuo progetto — niente snippet a metà che poi non compilano.

Requisiti e Installazione

Prerequisiti

Prima di iniziare, assicurati di avere:

  • React Native 0.76+ con la Nuova Architettura (Fabric) abilitata
  • Expo SDK 52+ se usi Expo (consigliato SDK 55)
  • Node.js 18+ e npm o yarn

Attenzione: Reanimated 4.x funziona esclusivamente con la Nuova Architettura di React Native. Se la tua app gira ancora sulla vecchia architettura (Paper), devi per forza restare con la versione 3.x oppure completare la migrazione prima di procedere. Non c'è via di mezzo, purtroppo.

Installazione con Expo

Con Expo l'installazione è davvero semplice — Expo gestisce automaticamente tutta la configurazione nativa:

npx expo install react-native-reanimated react-native-worklets

Una novità importante: i worklet sono stati estratti in un pacchetto separato chiamato react-native-worklets. Questo vuol dire che puoi usare i worklet anche senza Reanimated, per qualsiasi esecuzione JavaScript off-thread. Una scelta intelligente, secondo me.

Installazione con React Native CLI

npm install react-native-reanimated react-native-worklets
# oppure
yarn add react-native-reanimated react-native-worklets

Configurazione di Babel

Indipendentemente dal metodo di installazione, devi aggiungere il plugin Babel. Questo è un passaggio fondamentale — senza di esso i worklet semplicemente non funzionano:

// babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['react-native-worklets/plugin'], // DEVE essere l'ultimo plugin!
  };
};

Nota che il plugin è cambiato da react-native-reanimated/plugin a react-native-worklets/plugin. I vecchi import funzionano ancora per retrocompatibilità, ma sono deprecati — aggiorna appena puoi.

I Concetti Fondamentali

Shared Values: il Cuore delle Animazioni

Gli Shared Values sono il meccanismo di stato centrale di Reanimated. Sono valori mutabili che vivono sul thread UI e possono essere letti e modificati sia dal thread JavaScript che dal thread UI, senza serializzazione né Bridge. Questo è ciò che rende tutto così veloce.

import { useSharedValue } from 'react-native-reanimated';

function MioComponente() {
  // Crea un valore condiviso con valore iniziale 0
  const offset = useSharedValue(0);

  // Leggere il valore
  console.log(offset.value); // 0

  // Modificare il valore (scatena l'animazione)
  offset.value = 100;

  // Con React Compiler, usa get() e set()
  // offset.get() e offset.set(100)
}

A differenza di useState, modificare un shared value non causa un re-render React. L'aggiornamento è diretto e istantaneo sul thread UI — ed è proprio per questo che le animazioni risultano così fluide.

Animated Styles: Collegare i Valori alla UI

useAnimatedStyle crea un oggetto stile che si aggiorna automaticamente ogni volta che un shared value collegato cambia. Vediamo come usarlo:

import Animated, {
  useSharedValue,
  useAnimatedStyle,
} from 'react-native-reanimated';
import { Button, View, StyleSheet } from 'react-native';

function BoxAnimata() {
  const offset = useSharedValue(0);

  // Lo stile animato si aggiorna automaticamente
  const animatedStyles = useAnimatedStyle(() => ({
    transform: [{ translateX: offset.value }],
  }));

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.box, animatedStyles]} />
      <Button
        title="Muovi"
        onPress={() => {
          offset.value = Math.random() * 300;
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  box: { width: 80, height: 80, backgroundColor: '#6C63FF', borderRadius: 12 },
});

Suggerimento: definisci solo la parte dinamica degli stili con useAnimatedStyle. Mantieni gli stili statici in StyleSheet per evitare ricalcoli inutili — è una di quelle cose piccole che fanno davvero la differenza in termini di performance.

Worklet: Funzioni che Girano sul Thread UI

I worklet sono funzioni speciali che vengono eseguite direttamente sul thread UI. Sono il motivo per cui Reanimated riesce a garantire animazioni a 60 fps anche quando il thread JavaScript è impegnato con operazioni pesanti (tipo fetch di dati o calcoli complessi).

import { runOnUI, runOnJS } from 'react-native-worklets';

// Questa funzione gira sul thread UI
function aggiornaUI() {
  'worklet';
  // Operazioni sul thread UI...
  console.log('Eseguito sul thread UI!');
}

// Per richiamare una funzione JS dal thread UI
function mostraLog(messaggio) {
  console.log(messaggio);
}

function workletConCallback() {
  'worklet';
  // Usa runOnJS per comunicare col thread JavaScript
  runOnJS(mostraLog)('Ciao dal thread UI!');
}

Le Primitive di Animazione

Allora, entriamo nel vivo. Reanimated 4 mette a disposizione tre primitive fondamentali per le animazioni. Vediamole una per una.

withTiming: Animazioni Basate sul Tempo

withTiming crea una transizione da un valore all'altro in un tempo definito, con supporto per diverse curve di easing:

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  Easing,
} from 'react-native-reanimated';

function AnimazioneTiming() {
  const larghezza = useSharedValue(100);

  const config = {
    duration: 500,
    easing: Easing.bezier(0.25, 0.1, 0.25, 1),
  };

  const stileAnimato = useAnimatedStyle(() => ({
    width: withTiming(larghezza.value, config),
  }));

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.barra, stileAnimato]} />
      <Button
        title="Espandi"
        onPress={() => {
          larghezza.value = larghezza.value === 100 ? 300 : 100;
        }}
      />
    </View>
  );
}

withSpring: Animazioni con Effetto Molla

withSpring crea animazioni basate sulla fisica, con un effetto di rimbalzo naturale. Devo dire che è la mia primitiva preferita — il movimento che produce sembra semplicemente più "vivo" rispetto a un timing lineare. La versione 4 ha anche semplificato la configurazione:

import { withSpring } from 'react-native-reanimated';

// Configurazione semplice
offset.value = withSpring(200);

// Configurazione personalizzata
offset.value = withSpring(200, {
  damping: 10,       // Smorzamento (più basso = più rimbalzo)
  stiffness: 100,    // Rigidità della molla
  mass: 1,           // Massa dell'oggetto

  // NOVITÀ v4: energyThreshold sostituisce
  // restDisplacementThreshold e restSpeedThreshold
  energyThreshold: 0.01,
});

Nota sulla migrazione: in Reanimated 4, i parametri restDisplacementThreshold e restSpeedThreshold sono stati sostituiti da un singolo energyThreshold. Inoltre, il parametro duration ora rappresenta la durata percepita dell'animazione, non il tempo effettivo di completamento. Un dettaglio sottile ma importante.

withDecay: Animazioni con Inerzia

withDecay è perfetto per simulare il momentum — ad esempio quando l'utente rilascia un elemento dopo averlo trascinato e vuoi che continui a muoversi con decelerazione naturale:

import { withDecay } from 'react-native-reanimated';

// Tipicamente usato dopo una gesture
offset.value = withDecay({
  velocity: velocitaDalGesto,    // Velocità iniziale
  deceleration: 0.998,           // Fattore di decelerazione
  rubberBandEffect: true,        // Effetto elastico ai bordi
  clamp: [0, 500],               // Limiti di movimento
});

Combinare le Animazioni: Sequenze e Ripetizioni

Qui le cose si fanno interessanti. Reanimated offre utility per creare animazioni complesse combinando le primitive che abbiamo appena visto:

import {
  withSequence,
  withDelay,
  withRepeat,
  withSpring,
  withTiming,
} from 'react-native-reanimated';

// Sequenza: una dopo l'altra
scala.value = withSequence(
  withTiming(1.2, { duration: 200 }),  // Ingrandisci
  withTiming(0.9, { duration: 150 }),  // Rimpicciolisci
  withSpring(1)                         // Torna alla dimensione originale
);

// Ritardo prima di un'animazione
opacita.value = withDelay(
  500,                                  // Aspetta 500ms
  withTiming(1, { duration: 300 })     // Poi fade in
);

// Ripetizione
rotazione.value = withRepeat(
  withTiming(360, { duration: 1000 }), // Animazione base
  -1,                                    // -1 = ripeti all'infinito
  false                                  // false = non invertire
);

La Grande Novità: Animazioni CSS Dichiarative

Ok, questa è probabilmente la feature più interessante di Reanimated 4. Ora puoi definire animazioni e transizioni usando una sintassi CSS familiare, direttamente negli stili dei componenti. Niente shared value, niente useAnimatedStyle — solo stili dichiarativi. Se vieni dal mondo web, ti sentirai subito a casa.

CSS Transitions

Le transizioni CSS permettono di animare automaticamente i cambiamenti di proprietà di stile. Dichiari quali proprietà devono essere animate e come — il resto lo fa Reanimated:

import React, { useState } from 'react';
import { Pressable, View, StyleSheet } from 'react-native';
import Animated from 'react-native-reanimated';

function CartaEspandibile() {
  const [aperta, setAperta] = useState(false);

  return (
    <View style={styles.container}>
      <Pressable onPress={() => setAperta(!aperta)}>
        <Animated.View
          style={{
            width: aperta ? 300 : 160,
            height: aperta ? 200 : 100,
            backgroundColor: aperta ? '#6C63FF' : '#A29BFE',
            borderRadius: aperta ? 24 : 12,
            // Transizioni CSS — la magia avviene qui!
            transitionProperty: ['width', 'height', 'backgroundColor', 'borderRadius'],
            transitionDuration: 400,
            transitionTimingFunction: 'ease-in-out',
          }}
        />
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
});

È esattamente come scrivere transizioni CSS sul web. Le proprietà supportate includono:

  • transitionProperty — array delle proprietà da animare
  • transitionDuration — durata in millisecondi
  • transitionDelay — ritardo prima dell'inizio
  • transitionTimingFunction — curva di easing ('linear', 'ease-in', 'ease-out', 'ease-in-out')
  • transitionBehavior — comportamento della transizione

CSS Keyframe Animations

Per animazioni più complesse, con più passaggi intermedi, puoi usare i keyframe CSS. L'API ricorda molto quella che useresti in un foglio di stile web:

import Animated from 'react-native-reanimated';

// Definisci i keyframe come oggetto
const pulsazione = {
  from: { transform: [{ scale: 1 }], opacity: 1 },
  '50%': { transform: [{ scale: 1.15 }], opacity: 0.8 },
  to: { transform: [{ scale: 1 }], opacity: 1 },
};

function BottonePulsante() {
  return (
    <Animated.View
      style={{
        width: 80,
        height: 80,
        backgroundColor: '#FF6B6B',
        borderRadius: 40,
        // Animazione keyframe CSS
        animationName: pulsazione,
        animationDuration: 1500,
        animationIterationCount: 'infinite',
        animationTimingFunction: 'ease-in-out',
      }}
    />
  );
}

Le proprietà di animazione CSS supportate sono:

  • animationName — l'oggetto keyframe
  • animationDuration — durata di un ciclo
  • animationDelay — ritardo iniziale
  • animationIterationCount — numero o 'infinite'
  • animationDirection — 'normal', 'reverse', 'alternate'
  • animationFillMode — 'none', 'forwards', 'backwards', 'both'
  • animationTimingFunction — curva di easing

Preset Pronti all'Uso: react-native-css-animations

Software Mansion ha pubblicato anche un pacchetto con animazioni CSS preconfigurate, così non devi reinventare la ruota per i casi d'uso più comuni:

yarn add react-native-css-animations

I preset disponibili includono:

  • spin — rotazione lineare continua, perfetto per loader
  • ping — scala e dissolvenza, ottimo per notifiche
  • pulse — dissolvenza in entrata e uscita, ideale per skeleton loader
  • shimmer — animazione da sinistra a destra, il classico effetto di caricamento

Quando Usare CSS vs Worklet

Reanimated 4 ti dà due approcci. La domanda che mi fanno più spesso è: quale scelgo? Ecco una regola pratica:

  • CSS Transitions/Animations: per animazioni guidate da cambiamenti di stato. Sai il punto di partenza e di arrivo, e vuoi che la transizione avvenga automaticamente. Perfette per toggle, espansioni, fade, loading states.
  • Worklet + Shared Values: per animazioni interattive che richiedono controllo frame-by-frame. Gesture, effetti legati allo scroll, animazioni che dipendono dalla posizione del dito dell'utente in tempo reale.

La cosa bella è che puoi mescolarli nello stesso progetto, persino nello stesso componente. Le animazioni basate su shared value funzionano esattamente come prima, in parallelo con le nuove CSS animations.

Integrazione con Gesture Handler

Le animazioni più coinvolgenti sono quelle che rispondono ai gesti dell'utente. Reanimated si integra in modo naturale con React Native Gesture Handler (altra libreria di Software Mansion — questi ragazzi hanno costruito un ecosistema impressionante).

Setup

npx expo install react-native-gesture-handler
# oppure
npm install react-native-gesture-handler

Avvolgi la tua app con GestureHandlerRootView il più vicino possibile alla root:

import { GestureHandlerRootView } from 'react-native-gesture-handler';

export default function App() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <MiaApp />
    </GestureHandlerRootView>
  );
}

Esempio Pratico: Drag-and-Drop con Snap

Vediamo qualcosa di concreto. Creiamo un componente che l'utente può trascinare liberamente e che, quando rilasciato, torna alla posizione originale con un effetto molla:

import React from 'react';
import { StyleSheet, View } from 'react-native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';

function CartaDraggable() {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const scala = useSharedValue(1);

  // Salva la posizione iniziale per gestire gesti consecutivi
  const contestoX = useSharedValue(0);
  const contestoY = useSharedValue(0);

  const gestoPan = Gesture.Pan()
    .onStart(() => {
      // Salva la posizione attuale
      contestoX.value = translateX.value;
      contestoY.value = translateY.value;
      // Ingrandisci leggermente durante il trascinamento
      scala.value = withSpring(1.1);
    })
    .onChange((event) => {
      // Aggiorna posizione in tempo reale
      translateX.value = contestoX.value + event.translationX;
      translateY.value = contestoY.value + event.translationY;
    })
    .onFinalize(() => {
      // Torna alla posizione originale con effetto molla
      translateX.value = withSpring(0);
      translateY.value = withSpring(0);
      scala.value = withSpring(1);
    });

  const stileAnimato = useAnimatedStyle(() => ({
    transform: [
      { translateX: translateX.value },
      { translateY: translateY.value },
      { scale: scala.value },
    ],
  }));

  return (
    <View style={styles.container}>
      <GestureDetector gesture={gestoPan}>
        <Animated.View style={[styles.carta, stileAnimato]}>
          <Animated.Text style={styles.testo}>
            Trascinami!
          </Animated.Text>
        </Animated.View>
      </GestureDetector>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  carta: {
    width: 200,
    height: 120,
    backgroundColor: '#6C63FF',
    borderRadius: 16,
    alignItems: 'center',
    justifyContent: 'center',
    elevation: 8,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 4 },
    shadowOpacity: 0.3,
    shadowRadius: 8,
  },
  testo: { color: '#fff', fontSize: 18, fontWeight: 'bold' },
});

Gesto Tap con Feedback Visivo

Un altro pattern che uso spesso: dare un feedback visivo immediato quando l'utente preme un elemento.

import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

function BottoneConFeedback() {
  const premuto = useSharedValue(false);

  const gestoTap = Gesture.Tap()
    .onBegin(() => {
      premuto.value = true;
    })
    .onFinalize(() => {
      premuto.value = false;
    });

  const stileAnimato = useAnimatedStyle(() => ({
    transform: [
      { scale: withSpring(premuto.value ? 0.92 : 1) },
    ],
    backgroundColor: premuto.value ? '#5A52E0' : '#6C63FF',
  }));

  return (
    <GestureDetector gesture={gestoTap}>
      <Animated.View style={[styles.bottone, stileAnimato]}>
        <Animated.Text style={styles.testoBottone}>
          Premi qui
        </Animated.Text>
      </Animated.View>
    </GestureDetector>
  );
}

Layout Animations: Entrata e Uscita

Le Layout Animations permettono di animare automaticamente i componenti quando vengono aggiunti o rimossi dalla gerarchia della view. Nessuna configurazione manuale — basta usare le prop entering e exiting e Reanimated fa tutto da solo.

import React, { useState } from 'react';
import { Button, View, StyleSheet } from 'react-native';
import Animated, {
  FadeIn,
  FadeOut,
  SlideInRight,
  SlideOutLeft,
  BounceIn,
  ZoomOut,
  Layout,
} from 'react-native-reanimated';

function ListaAnimata() {
  const [elementi, setElementi] = useState(['Mela', 'Banana', 'Ciliegia']);

  const aggiungiElemento = () => {
    const frutti = ['Arancia', 'Uva', 'Kiwi', 'Mango', 'Pesca'];
    const random = frutti[Math.floor(Math.random() * frutti.length)];
    setElementi([...elementi, random]);
  };

  const rimuoviElemento = (indice) => {
    setElementi(elementi.filter((_, i) => i !== indice));
  };

  return (
    <View style={styles.container}>
      {elementi.map((elemento, indice) => (
        <Animated.View
          key={`${elemento}-${indice}`}
          entering={SlideInRight.delay(indice * 100)}
          exiting={SlideOutLeft}
          layout={Layout.springify()}
          style={styles.elemento}
        >
          <Animated.Text
            style={styles.testoElemento}
            onPress={() => rimuoviElemento(indice)}
          >
            {elemento} ✕
          </Animated.Text>
        </Animated.View>
      ))}
      <Button title="Aggiungi frutto" onPress={aggiungiElemento} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20, paddingTop: 60 },
  elemento: {
    backgroundColor: '#6C63FF',
    padding: 16,
    borderRadius: 12,
    marginBottom: 8,
  },
  testoElemento: { color: '#fff', fontSize: 16 },
});

Le animazioni predefinite disponibili sono tante: FadeIn, FadeOut, SlideInRight, SlideOutLeft, BounceIn, ZoomIn, FlipInX e molte altre. Ognuna può essere personalizzata con .delay(), .duration() e .springify().

Shared Element Transitions (v4.2.0)

Le Shared Element Transitions sono state la funzionalità più richiesta dalla community per anni, e con Reanimated 4.2.0 sono finalmente arrivate. Permettono di animare in modo fluido un componente da una schermata all'altra, dando all'utente una sensazione di continuità durante la navigazione. Pensate alle transizioni che vedete nelle app di Google — quel tipo di effetto.

Come Funzionano

Il concetto è semplice: assegni lo stesso sharedTransitionTag a componenti Animated.View su schermate diverse. Quando navighi tra queste schermate, Reanimated anima automaticamente la transizione tra i due elementi.

Requisiti

  • Reanimated 4.2.0 o successivo
  • @react-navigation/native-stack (lo stack JS-based non è supportato)
  • react-native-screens versione 4.19 o successiva
  • Abilitare il feature flag ENABLE_SHARED_ELEMENT_TRANSITIONS

Esempio Completo: Galleria con Transizione

import React from 'react';
import { View, Pressable, StyleSheet, Text } from 'react-native';
import Animated from 'react-native-reanimated';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

function ListaImmagini({ navigation }) {
  const immagini = [
    { id: '1', colore: '#FF6B6B', titolo: 'Tramonto' },
    { id: '2', colore: '#4ECDC4', titolo: 'Oceano' },
    { id: '3', colore: '#45B7D1', titolo: 'Cielo' },
  ];

  return (
    <View style={styles.lista}>
      {immagini.map((img) => (
        <Pressable
          key={img.id}
          onPress={() => navigation.navigate('Dettaglio', { immagine: img })}
        >
          <Animated.View
            sharedTransitionTag={`immagine-${img.id}`}
            style={[styles.miniatura, { backgroundColor: img.colore }]}
          >
            <Text style={styles.testoMiniatura}>{img.titolo}</Text>
          </Animated.View>
        </Pressable>
      ))}
    </View>
  );
}

function DettaglioImmagine({ route }) {
  const { immagine } = route.params;

  return (
    <View style={styles.dettaglio}>
      <Animated.View
        sharedTransitionTag={`immagine-${immagine.id}`}
        style={[styles.immagineGrande, { backgroundColor: immagine.colore }]}
      >
        <Text style={styles.testoGrande}>{immagine.titolo}</Text>
      </Animated.View>
    </View>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Lista" component={ListaImmagini} />
        <Stack.Screen name="Dettaglio" component={DettaglioImmagine} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

const styles = StyleSheet.create({
  lista: { flex: 1, padding: 16 },
  miniatura: {
    height: 120,
    borderRadius: 16,
    marginBottom: 12,
    justifyContent: 'center',
    alignItems: 'center',
  },
  testoMiniatura: { color: '#fff', fontSize: 18, fontWeight: '600' },
  dettaglio: { flex: 1 },
  immagineGrande: {
    height: 350,
    justifyContent: 'center',
    alignItems: 'center',
  },
  testoGrande: { color: '#fff', fontSize: 32, fontWeight: 'bold' },
});

Importante: il sharedTransitionTag deve essere univoco all'interno di una singola schermata, ma deve coincidere tra le schermate per identificare gli elementi corrispondenti.

Personalizzare la Transizione

import { SharedTransition, withSpring } from 'react-native-reanimated';

// Transizione con effetto molla
const transizioneMolla = SharedTransition.duration(550).springify();

// Transizione completamente personalizzata
const transizioneCustom = SharedTransition.custom((values) => {
  'worklet';
  return {
    height: withSpring(values.targetHeight),
    width: withSpring(values.targetWidth),
    originX: withSpring(values.targetOriginX),
    originY: withSpring(values.targetOriginY),
  };
});

// Applica con la prop sharedTransitionStyle
<Animated.View
  sharedTransitionTag="mio-tag"
  sharedTransitionStyle={transizioneMolla}
/>

Nota: le Shared Element Transitions sono ancora sperimentali. Non le consiglierei in produzione al momento — il Tab navigator non è supportato, e le transizioni con transparentModal danno problemi su iOS. Però per prototipi e demo funzionano davvero bene.

Migrazione da Reanimated 3.x a 4.x

Se stai già usando Reanimated 3.x, la buona notizia è che la migrazione alla versione 4 non è traumatica. Ecco cosa devi sapere.

Cosa Cambia

  • Pacchetto Worklets separato: installa react-native-worklets e aggiorna il plugin Babel da react-native-reanimated/plugin a react-native-worklets/plugin
  • withSpring: i parametri restDisplacementThreshold e restSpeedThreshold sono stati unificati in energyThreshold. Nella maggior parte dei casi, basta rimuovere i vecchi parametri e tutto funziona
  • Parametro duration: ora rappresenta la durata percepita dell'animazione, non quella effettiva. Potresti notare leggere differenze visive
  • Solo Nuova Architettura: se la tua app usa ancora Paper, non puoi aggiornare a Reanimated 4. Punto.

Cosa Resta Uguale

La buona notizia è che tutta la logica di animazione scritta con le API di Reanimated 2 o 3 funziona in 4.x con modifiche minime o nulle. Le animazioni basate su shared value continuano a funzionare come prima, e le nuove CSS animations possono essere adottate in modo incrementale — non devi riscrivere nulla se non vuoi.

Performance: Perché Reanimated è Così Veloce

Per capire perché Reanimated batte qualsiasi altra soluzione di animazione in React Native, bisogna guardare cosa succede sotto il cofano:

  • Thread UI nativo: tutte le animazioni girano sul thread UI, non su quello JavaScript. Se il tuo codice JS è impegnato a fare fetch di dati o calcoli pesanti, le animazioni non ne risentono minimamente
  • Nessun Bridge: grazie a JSI (JavaScript Interface) della Nuova Architettura, Reanimated comunica direttamente con il codice nativo senza serializzazione JSON
  • Shared Values: i valori sono condivisi tra thread senza copia — il thread UI legge e scrive direttamente gli stessi dati
  • CSS Animations ottimizzabili: essendo dichiarative, le animazioni CSS permettono a Reanimated di ottimizzare internamente cosa e come animare, riducendo il lavoro sul thread UI

Il risultato pratico? Animazioni a 60-120 fps anche su dispositivi di fascia bassa, dove altre soluzioni mostrerebbero stuttering evidente. L'ho testato su un vecchio telefono Android e la differenza si vede eccome.

FAQ — Domande Frequenti

Reanimated 4 funziona con la vecchia architettura di React Native?

No. Reanimated 4.x supporta esclusivamente la Nuova Architettura (Fabric). Se la tua app gira ancora sulla vecchia architettura, devi restare con Reanimated 3.x oppure migrare alla Nuova Architettura. Reanimated 3 è comunque ancora attivamente mantenuto e riceve aggiornamenti di sicurezza.

Qual è la differenza tra withTiming e withSpring?

withTiming crea un'animazione con una durata fissa e una curva di easing configurabile. withSpring crea un'animazione basata sulla fisica con effetto molla — non ha una durata fissa ma parametri fisici come smorzamento, rigidità e massa. In breve: usa withTiming quando ti serve controllo preciso sulla durata; usa withSpring quando vuoi un movimento che sembri naturale.

Posso usare le nuove CSS Animations insieme ai worklet tradizionali?

Assolutamente sì. Le animazioni CSS e quelle basate su shared value/worklet funzionano simultaneamente e possono coesistere nello stesso componente. Puoi adottare le CSS animations in modo incrementale, senza riscrivere nulla del codice esistente.

Le Shared Element Transitions sono pronte per la produzione?

Non ancora (marzo 2026). Sono una feature sperimentale protetta da un feature flag. Funzionano bene in molti casi, ma hanno limitazioni importanti: niente supporto per Tab navigator, problemi con le transizioni modali su iOS, e supporto parziale per le animazioni custom. Per prototipi vanno benissimo, ma per la produzione meglio aspettare la stabilizzazione.

Devo installare react-native-worklets separatamente?

Sì. A partire da Reanimated 4, i worklet vivono nel pacchetto separato react-native-worklets. Devi installarlo e aggiornare il plugin Babel. I vecchi import da react-native-reanimated funzionano ancora per retrocompatibilità, ma sono deprecati e verranno rimossi nelle prossime release.

Sull'Autore Editorial Team

Our team of expert writers and editors.