Dlaczego animacje decydują o sukcesie Twojej aplikacji mobilnej
Pomyśl przez chwilę o swoich ulubionych aplikacjach mobilnych. Co je łączy? Płynne przejścia między ekranami, natychmiastowa reakcja na dotyk, subtelne animacje ładowania — to wszystko sprawia, że aplikacja po prostu czuje się jak natywna. W React Native osiągnięcie takiego poziomu płynności było kiedyś sporym wyzwaniem. Wbudowane API Animated działało na wątku JavaScript, więc każdy spadek wydajności JS natychmiast zacinał animacje. Frustrujące, prawda?
I właśnie tutaj wkracza React Native Reanimated 4.
Ta biblioteka całkowicie zmieniła podejście do animacji w ekosystemie React Native. Wersja 4, wydana w lipcu 2025 roku, przyniosła ze sobą coś, na co wielu czekało od lat: animacje CSS znane z przeglądarek internetowych, teraz dostępne natywnie na iOS i Android. A najnowsze wydanie 4.2.2 dorzuca jeszcze Shared Element Transitions — szczerze mówiąc, najbardziej wyczekiwaną funkcję w historii tej biblioteki.
W tym przewodniku przeprowadzimy Cię przez wszystkie kluczowe aspekty Reanimated 4 — od instalacji, przez animacje CSS i worklety, aż po integrację z gestami i przejścia między ekranami. Każda sekcja zawiera działające przykłady kodu, które możesz skopiować prosto do swojego projektu.
Co nowego w Reanimated 4 — przegląd zmian
Reanimated 4 to nie zwykła aktualizacja. To fundamentalna zmiana architektury i filozofii całej biblioteki. Oto najważniejsze nowości.
Animacje i przejścia CSS
To flagowa funkcja wersji 4 i — moim zdaniem — prawdziwy game changer. Zamiast ręcznie zarządzać wartościami animacji za pomocą hooków i shared values, możesz teraz deklaratywnie definiować animacje używając składni niemal identycznej z CSS. Ustawiasz transitionProperty, transitionDuration i transitionTimingFunction bezpośrednio w stylach komponentu — a Reanimated zajmuje się resztą. Wszystko działa na wątku UI z płynnością 60+ FPS.
Worklety wydzielone do osobnego pakietu
Worklety — czyli specjalne funkcje uruchamiane na wątku UI — zostały przeniesione do nowego pakietu react-native-worklets. I to nie tylko kwestia porządku w kodzie. Dzięki temu inne biblioteki (np. react-native-vision-camera) mogą korzystać z technologii workletów niezależnie od Reanimated. Efekt? Mniejszy bundle size i lepsza modularność.
Wyłączne wsparcie dla Nowej Architektury (Fabric)
Tu ważna uwaga: Reanimated 4.x działa wyłącznie z Nową Architekturą React Native (Fabric). Wymaga React Native 0.76 lub nowszego. Jeśli Twoja aplikacja nadal korzysta ze Starej Architektury, powinieneś zostać przy Reanimated 3.x lub — co zdecydowanie polecam — przeprowadzić migrację na Nową Architekturę. W 2026 roku to już naprawdę pora.
Shared Element Transitions (wersja 4.2.0+)
Najbardziej wyczekiwana funkcja w historii Reanimated. Pozwala animować elementy między dwoma ekranami nawigacyjnymi, tworząc wrażenie ciągłości i profesjonalnego dopracowania. Na iOS wspiera nawet animacje kierowane gestem przesunięcia wstecz — tego brakowało mi od dawna.
Instalacja i konfiguracja
Konfiguracja Reanimated 4 różni się w zależności od tego, czy korzystasz z Expo czy bare React Native CLI. Przejdźmy przez obie ścieżki.
Instalacja z Expo (zalecane)
Jeśli korzystasz z Expo SDK 53+, instalacja jest banalna. Plugin Babel jest automatycznie konfigurowany przez babel-preset-expo.
npx expo install react-native-reanimated react-native-worklets
I to dosłownie wszystko. Nie musisz ręcznie modyfikować babel.config.js — Expo zajmie się tym za Ciebie. Po instalacji zrestartuj serwer developerski:
npx expo start --clear
Instalacja z React Native CLI
W przypadku bare React Native musisz zainstalować oba pakiety i ręcznie skonfigurować plugin Babel. Trochę więcej roboty, ale nic skomplikowanego:
npm install react-native-reanimated react-native-worklets
Następnie zaktualizuj plik babel.config.js:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-worklets/plugin'],
};
Uwaga: Jeśli migrujesz z Reanimated 3, zamień react-native-reanimated/plugin na react-native-worklets/plugin w konfiguracji Babel. Stary plugin nie jest już kompatybilny z wersją 4.
Po zmianie konfiguracji przebuduj aplikację na obu platformach:
cd ios && pod install && cd ..
npx react-native run-android
npx react-native run-ios
Animacje CSS — deklaratywne podejście do animacji
No dobra, przejdźmy do najciekawszej części. Animacje CSS to flagowa nowość Reanimated 4 i prawdopodobnie największa zmiana w podejściu do animacji w React Native od lat. Zamiast imperatywnie zarządzać wartościami animacji, po prostu deklarujesz co ma się animować i jak — a resztą zajmuje się biblioteka.
CSS Transitions — animowanie zmian stanu
Transitions to najprostszy sposób na dodanie animacji do komponentu. Definiujesz, które właściwości mają być animowane, jak długo trwa przejście i jaką funkcję easing zastosować. Potem zmieniasz stan — a przejście dzieje się automatycznie. Bez dodatkowego kodu.
import React, { useState } from 'react';
import { Pressable, SafeAreaView, Text, StyleSheet } from 'react-native';
import Animated from 'react-native-reanimated';
export default function CSSTransitionExample() {
const [expanded, setExpanded] = useState(false);
return (
<SafeAreaView style={styles.container}>
<Pressable onPress={() => setExpanded(prev => !prev)}>
<Animated.View
style={{
width: expanded ? 300 : 150,
height: expanded ? 200 : 100,
backgroundColor: expanded ? '#6366f1' : '#818cf8',
borderRadius: expanded ? 24 : 12,
justifyContent: 'center',
alignItems: 'center',
// Reanimated 4 CSS Transitions:
transitionProperty: ['width', 'height', 'backgroundColor', 'borderRadius'],
transitionDuration: 400,
transitionTimingFunction: 'ease-in-out',
}}>
<Text style={styles.label}>
{expanded ? 'Zwiń' : 'Rozwiń'}
</Text>
</Animated.View>
</Pressable>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
label: { color: 'white', fontWeight: 'bold', fontSize: 16 },
});
Zwróć uwagę na kluczowe właściwości: transitionProperty to tablica właściwości do animowania, transitionDuration określa czas trwania w milisekundach, a transitionTimingFunction definiuje krzywą easing. Obsługiwane wartości to m.in. ease, ease-in, ease-out, ease-in-out i linear.
CSS Keyframe Animations — wieloetapowe sekwencje
Gdy potrzebujesz bardziej złożonych, wieloetapowych animacji, CSS keyframes dają Ci precyzyjną kontrolę nad każdym etapem. Definiujesz obiekt, w którym klucze (od 0 do 100) reprezentują procent postępu animacji.
import React from 'react';
import { StyleSheet, View } from 'react-native';
import Animated from 'react-native-reanimated';
const pulseKeyframes = {
0: { transform: [{ scale: 1 }], opacity: 1 },
50: { transform: [{ scale: 1.15 }], opacity: 0.7 },
100: { transform: [{ scale: 1 }], opacity: 1 },
};
export default function PulseAnimation() {
return (
<View style={styles.container}>
<Animated.View
style={[
styles.circle,
{
animationName: pulseKeyframes,
animationDuration: 2000,
animationIterationCount: 'infinite',
animationTimingFunction: 'ease-in-out',
},
]}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
circle: {
width: 100,
height: 100,
borderRadius: 50,
backgroundColor: '#6366f1',
},
});
Keyframes obsługują też animationIterationCount (liczba powtórzeń lub infinite), animationDirection (np. alternate dla animacji w obie strony) i animationPlayState do pauzowania i wznawiania. Całkiem sporo możliwości jak na tak proste API.
Gotowe presety — react-native-css-animations
Jeśli nie chcesz pisać keyframes od zera (a kto chce, skoro nie musi?), Software Mansion udostępnia bibliotekę react-native-css-animations z gotowymi presetami wzorowanymi na Tailwind CSS:
npm install react-native-css-animations
import { pulse, bounce, shimmer } from 'react-native-css-animations';
import Animated from 'react-native-reanimated';
// Efekt pulsowania — idealny do skeletonów ładowania
<Animated.View style={[styles.skeleton, pulse]} />
// Efekt odbijania — świetny na wskaźnik scrollowania
<Animated.View style={[styles.arrow, bounce]} />
// Efekt shimmer — klasyczny loader
<Animated.View style={[styles.placeholder, shimmer]} />
Worklety i Shared Values — imperatywne podejście do animacji
Animacje CSS są super do prostych przejść stanów, ale co gdy potrzebujesz pełnej kontroli nad każdą klatką? Gdy chcesz reagować na gesty w czasie rzeczywistym albo budować animacje oparte na fizyce sprężyn? Tutaj wchodzą worklety i shared values — fundament, na którym Reanimated budowano od wersji 2.
Shared Values — stan poza Reactem
Shared values to specjalne zmienne, które żyją poza drzewem React. Mogą być odczytywane i modyfikowane zarówno z wątku JavaScript, jak i z wątku UI — bez żadnego narzutu komunikacyjnego. To klucz do 60 FPS: zmiana shared value nie wywołuje re-renderu React. I to właśnie czyni je tak potężnymi.
import { useSharedValue, useAnimatedStyle } from 'react-native-reanimated';
function MyComponent() {
const offset = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: offset.value }],
}));
return <Animated.View style={[styles.box, animatedStyle]} />;
}
withTiming — animacje czasowe
withTiming to podstawowa funkcja animacji w Reanimated. Animuje shared value od bieżącej wartości do docelowej w określonym czasie, z wybraną krzywą easing. Prosta, ale zaskakująco wszechstronna.
import {
useSharedValue,
useAnimatedStyle,
withTiming,
Easing,
} from 'react-native-reanimated';
function FadeInBox() {
const opacity = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
const handlePress = () => {
opacity.value = withTiming(1, {
duration: 600,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
});
};
return (
<Pressable onPress={handlePress}>
<Animated.View style={[styles.box, animatedStyle]} />
</Pressable>
);
}
Moduł Easing udostępnia gotowe funkcje: Easing.linear, Easing.ease, Easing.bounce, Easing.elastic(), a także modyfikatory Easing.in(), Easing.out() i Easing.inOut().
withSpring — animacje oparte na fizyce
withSpring to animacja symulująca zachowanie sprężyny. Nie przyjmuje parametru duration — czas trwania wynika z parametrów fizycznych: masy (mass), sztywności (stiffness) i tłumienia (damping). Osobiście uważam, że sprężynowe animacje dają najlepsze efekty w interakcjach dotykowych.
import { useSharedValue, withSpring } from 'react-native-reanimated';
function SpringButton() {
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
const handlePressIn = () => {
scale.value = withSpring(0.9, {
mass: 1,
stiffness: 200,
damping: 10,
});
};
const handlePressOut = () => {
scale.value = withSpring(1, {
mass: 1,
stiffness: 200,
damping: 10,
});
};
return (
<Pressable onPressIn={handlePressIn} onPressOut={handlePressOut}>
<Animated.View style={[styles.button, animatedStyle]}>
<Text>Naciśnij mnie</Text>
</Animated.View>
</Pressable>
);
}
Animacje sprężynowe reagują na prędkość gestu i tworzą naturalne, fizyczne wrażenie ruchu. Gdy raz zaczniesz ich używać, trudno wrócić do zwykłego withTiming w kontekście interakcji użytkownika.
Modyfikatory — sekwencje, powtórzenia, opóźnienia
Reanimated dostarcza trzy modyfikatory do łączenia animacji w złożone sekwencje:
withSequence— uruchamia animacje jedna po drugiejwithRepeat— powtarza animację określoną liczbę razy (lub nieskończenie)withDelay— dodaje opóźnienie przed startem animacji
import {
withSequence,
withRepeat,
withDelay,
withTiming,
} from 'react-native-reanimated';
// Efekt potrząsania — np. walidacja formularza
const shakeAnimation = () => {
const OFFSET = 10;
const DURATION = 80;
translateX.value = withSequence(
withTiming(-OFFSET, { duration: DURATION }),
withRepeat(
withTiming(OFFSET, { duration: DURATION }),
5, // 5 powtórzeń
true // reverse — animacja w obie strony
),
withTiming(0, { duration: DURATION })
);
};
// Animacja z opóźnieniem
const delayedFadeIn = () => {
opacity.value = withDelay(
500, // 500ms opóźnienia
withTiming(1, { duration: 400 })
);
};
Integracja z React Native Gesture Handler
Sama animacja to dopiero połowa sukcesu. Druga połowa? Reakcja na gesty użytkownika — dotyk, przeciąganie, szczypanie. Reanimated 4 ściśle integruje się z biblioteką react-native-gesture-handler (też od Software Mansion), umożliwiając budowanie animacji sterowanych gestami bezpośrednio na wątku UI.
Instalacja Gesture Handler
# Expo
npx expo install react-native-gesture-handler
# React Native CLI
npm install react-native-gesture-handler
Przeciąganie elementu — Pan Gesture
Oto kompletny przykład elementu, który można przeciągać palcem i który po puszczeniu wraca na miejsce z efektem sprężyny. To jeden z tych wzorców, które wykorzystuję praktycznie w każdym projekcie:
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';
export default function DraggableBox() {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const savedX = useSharedValue(0);
const savedY = useSharedValue(0);
const panGesture = Gesture.Pan()
.onUpdate((event) => {
translateX.value = savedX.value + event.translationX;
translateY.value = savedY.value + event.translationY;
})
.onEnd(() => {
// Wracamy na pozycję startową z efektem sprężyny
translateX.value = withSpring(0);
translateY.value = withSpring(0);
savedX.value = 0;
savedY.value = 0;
});
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
}));
return (
<View style={styles.container}>
<GestureDetector gesture={panGesture}>
<Animated.View style={[styles.box, animatedStyle]} />
</GestureDetector>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
box: {
width: 120,
height: 120,
backgroundColor: '#6366f1',
borderRadius: 16,
},
});
Efekt flick z withDecay
Funkcja withDecay symuluje ruch z narastającym oporem — idealnie nadaje się do gestów typu flick albo swipe. Element porusza się z prędkością gestu i stopniowo zwalnia, dokładnie tak jak przewijanie listy w natywnej aplikacji:
import { withDecay } from 'react-native-reanimated';
import { Gesture } from 'react-native-gesture-handler';
const flickGesture = Gesture.Pan()
.onUpdate((event) => {
translateX.value = event.translationX;
})
.onEnd((event) => {
translateX.value = withDecay({
velocity: event.velocityX, // Prędkość rzutu
clamp: [-200, 200], // Ograniczenie zakresu ruchu
deceleration: 0.998, // Współczynnik spowolnienia
});
});
Shared Element Transitions — animacje między ekranami
Shared Element Transitions to funkcja wprowadzona w Reanimated 4.2.0, która pozwala animować elementy między dwoma ekranami nawigacyjnymi. Efekt? Na przykład miniaturka produktu na liście płynnie przechodzi w duże zdjęcie na ekranie szczegółów — dając użytkownikowi poczucie ciągłości interfejsu. To naprawdę robi wrażenie.
Jak to działa
Reanimated monitoruje zmiany stosu nawigacyjnego. Gdy top screen się zmienia, sprawdza, czy na starym i nowym ekranie istnieją komponenty z tym samym atrybutem sharedTransitionTag. Jeśli tak — automatycznie animuje przejście między nimi. Brzmi prosto i (w większości przypadków) takie właśnie jest.
Konfiguracja
Żeby korzystać z Shared Element Transitions, musisz włączyć flagę funkcji w pliku głównym aplikacji:
import { LayoutAnimationConfig } from 'react-native-reanimated';
// Włącz Shared Element Transitions
LayoutAnimationConfig.ENABLE_SHARED_ELEMENT_TRANSITIONS = true;
Przykład implementacji
import Animated, { SharedTransition } from 'react-native-reanimated';
// Ekran listy
function ProductList({ navigation }) {
return (
<FlatList
data={products}
renderItem={({ item }) => (
<Pressable onPress={() => navigation.navigate('Details', { id: item.id })}>
<Animated.Image
source={{ uri: item.thumbnail }}
style={styles.thumbnail}
sharedTransitionTag={`product-image-${item.id}`}
/>
<Text>{item.name}</Text>
</Pressable>
)}
/>
);
}
// Ekran szczegółów
function ProductDetails({ route }) {
const { id } = route.params;
const product = getProduct(id);
return (
<ScrollView>
<Animated.Image
source={{ uri: product.image }}
style={styles.fullImage}
sharedTransitionTag={`product-image-${id}`}
/>
<Text>{product.description}</Text>
</ScrollView>
);
}
Na iOS, gdy animacja jest wywoływana gestem przesunięcia wstecz (swipe back), Reanimated automatycznie przełącza się na animację opartą na postępie — przejście reaguje na ruch palca i pozwala na anulowanie nawigacji. Dokładnie tak, jak w natywnych aplikacjach Apple.
CSS Animations vs. Worklety — kiedy używać czego
Reanimated 4 daje Ci dwa potężne systemy animacji. Pytanie brzmi: kiedy sięgnąć po który?
Kiedy używać CSS Animations
- Przejścia między dwoma stanami UI (rozwinięcie/zwinięcie, show/hide)
- Animacje ładowania (pulse, shimmer, skeleton screens)
- Proste efekty hover i press
- Animacje wejścia i wyjścia komponentów
- Gdy zależy Ci na czytelności kodu i szybkości implementacji
Kiedy używać Workletów
- Animacje sterowane gestami (przeciąganie, pinch-to-zoom)
- Animacje reagujące na scroll (parallax, sticky headers)
- Animacje zależne od sensorów (akcelerometr, żyroskop)
- Złożone interakcje wymagające kontroli klatka-po-klatce
- Animacje z logiką warunkową (np. snap points)
Zasada kciuka: zacznij od CSS Transitions. Są prostsze w pisaniu, łatwiejsze w utrzymaniu i wystarczają dla zdecydowanej większości animacji UI. Po worklety sięgaj dopiero, gdy potrzebujesz kontroli gestowej, efektów scrollowych albo aktualizacji klatka-po-klatce.
Co ważne — oba systemy współistnieją. Możesz mieszać animacje CSS i workletowe w tej samej aplikacji, a nawet w tym samym komponencie. Adopcja CSS API jest inkrementalna, więc nie musisz przepisywać istniejących animacji.
Migracja z Reanimated 3 do 4
Jeśli korzystasz z Reanimated 3, migracja do wersji 4 jest relatywnie bezbolesna. Wersja 4 zachowuje pełną kompatybilność wsteczną z API workletowym — Twoje istniejące animacje używające useSharedValue, useAnimatedStyle, withSpring i withTiming będą działać bez zmian. To duży plus.
Kroki migracji
- Zainstaluj
react-native-worklets— nowy pakiet z workletami - Zaktualizuj plugin Babel — zmień
react-native-reanimated/pluginnareact-native-worklets/plugin - Zamień
useScrollViewOffsetnauseScrollOffset(stara nazwa jest deprecated) - Upewnij się, że korzystasz z Nowej Architektury (Fabric) — Reanimated 4 nie obsługuje Starej Architektury
- Przebuduj aplikację na obu platformach
Importy workletów z react-native-reanimated nadal działają (są reeksportowane), ale są oznaczone jako deprecated i zostaną usunięte w przyszłym wydaniu. Warto zacząć stopniowo przenosić importy do react-native-worklets — lepiej nie zostawiać tego na ostatnią chwilę.
Wskazówki wydajnościowe
Reanimated domyślnie uruchamia animacje na wątku UI, co zapewnia płynność 60+ FPS. Ale jest kilka dodatkowych kroków, które pomogą wycisnąć z niego maksimum:
- Używaj
Animated.Viewzamiast zwykłegoView— tylko opakowane komponenty mogą być animowane na wątku UI - Unikaj
runOnJSw gorących ścieżkach — przeskoki między wątkami UI i JS dodają opóźnienia (to częsty błąd) - Używaj selektorów w
useAnimatedStyle— odczytuj tylko te shared values, które faktycznie wpływają na styl - Rozważ nowe flagi wydajnościowe z wersji 4.2.0:
FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS— synchronizuje zakończone animacje z React, redukując pracę przy następnych aktualizacjachUSE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS— uruchamia reconciliation tylko dla aktualizacji React, pomijając kosztowną synchronizację przy scrollowaniu
- Preferuj
transformiopacity— te właściwości są animowane przez GPU i nie wymagają przeliczania layoutu
Najczęściej zadawane pytania (FAQ)
Czy Reanimated 4 działa z Expo?
Tak, w pełni. Reanimated 4 jest kompatybilny z Expo SDK 53+. Instalacja wymaga jedynie npx expo install react-native-reanimated react-native-worklets. Plugin Babel jest automatycznie konfigurowany przez babel-preset-expo, więc nie musisz nic ręcznie zmieniać w babel.config.js.
Czy muszę migrować na Nową Architekturę, żeby używać Reanimated 4?
Niestety tak — tu nie ma obejścia. Reanimated 4.x działa wyłącznie z Nową Architekturą React Native (Fabric) i wymaga React Native 0.76 lub nowszego. Jeśli Twoja aplikacja nadal korzysta ze Starej Architektury, zostań przy Reanimated 3.x (nadal jest aktywnie utrzymywany) lub przeprowadź migrację.
Czy moje istniejące animacje z Reanimated 3 będą działać po aktualizacji?
Tak, będą. Reanimated 4 zachowuje pełną kompatybilność wsteczną. Animacje oparte na shared values, useAnimatedStyle, withSpring, withTiming i innych hookach z wersji 3 działają bez zmian. Jedyne co musisz zrobić to zaktualizować plugin Babel i zainstalować react-native-worklets.
Jaka jest różnica między animacjami CSS a workletami w Reanimated 4?
W skrócie: animacje CSS działają deklaratywnie — definiujesz co i jak ma się animować, a Reanimated zajmuje się wykonaniem. Worklety dają Ci imperatywną kontrolę klatka-po-klatce. CSS sprawdzają się idealnie w przejściach stanów UI i animacjach ładowania. Worklety są niezbędne przy animacjach gestowych, efektach scrollowych i złożonych interakcjach. Oba systemy działają na wątku UI i zapewniają 60+ FPS.
Czy mogę używać animacji CSS i workletów jednocześnie w jednej aplikacji?
Jak najbardziej. Oba systemy animacji współistnieją i możesz je mieszać nawet w obrębie jednego komponentu. Reanimated 4 pozwala na stopniową adopcję CSS API — nie musisz przepisywać istniejących animacji opartych na workletach, żeby korzystać z nowych funkcji CSS.