Hubert
12 min
16 kwietnia, 2025

React Native Reanimated - wprowadzenie do płynnych i wydajnych animacji w aplikacjach mobilnych

Chcesz tworzyć płynne, responsywne animacje w React Native, które naprawdę wyglądają i działają jak natywne? Poznaj React Native Reanimated — potężną bibliotekę do animacji działającą poza wątkiem JavaScript. W tym artykule krok po kroku wyjaśniamy, jak działa, kiedy warto jej używać i jak zacząć tworzyć nowoczesne, wydajne interfejsy mobilne z użyciem Reanimated.

Czytaj więcej
React Native Reanimated - wprowadzenie do płynnych i wydajnych animacji w aplikacjach mobilnych

Co to jest React Native Reanimated?

React Native Reanimated to zaawansowana biblioteka do tworzenia wydajnych, natywnych animacji w aplikacjach zbudowanych na React Native. Została opracowana i jest rozwijana przez Software Mansion – polską firmę specjalizującą się w tworzeniu narzędzi dla środowiska React Native, która stoi również za takimi projektami jak react-native-gesture-handler czy react-native-screens.

Kiedy i dlaczego warto używać React Native Reanimated?

React Native Reanimated sprawdza się najlepiej wtedy, gdy zależy nam na płynnych, responsywnych animacjach, które nie zawodzą nawet w bardziej złożonych interfejsach użytkownika. W przeciwieństwie do standardowego API Animated, Reanimated działa na natywnym wątku UI, co oznacza, że animacje nie są zależne od wydajności JavaScriptu.

Dzięki temu:

  • animacje nie przycinają się, nawet gdy aplikacja wykonuje intensywne operacje w tle,
  • możemy bezproblemowo łączyć je z gestami (np. przeciągnięcia, przesunięcia, dociągnięcia),
  • zapewniają doświadczenie użytkownika zbliżone do natywnych aplikacji,
  • sprawdzają się w komponentach takich jak bottom sheets, carousels, swipeable lists, drag & drop i wielu innych.

Warto sięgnąć po Reanimated, gdy:

  • klasyczne Animated zaczyna się dławić lub jest trudne do rozbudowy,
  • zależy Ci na nowoczesnym, płynnym UI (np. animacje ekranów, przejść, modali),
  • tworzysz interfejsy silnie zależne od gestów i dotyku.

To narzędzie stworzone z myślą o aplikacjach, które mają „czuć się” jak natywne — niezależnie od platformy.

Podstawowe pojęcia i koncepcje

Aby skutecznie korzystać z React Native Reanimated, warto zrozumieć kilka kluczowych koncepcji, które odróżniają tę bibliotekę od standardowego podejścia w React Native:

1. useSharedValue

To specjalny typ wartości, który żyje poza wątkiem JavaScript i jest współdzielony z natywnym UI. Używamy go do przechowywania zmiennych, które będą animowane (np. pozycja, skala, przezroczystość).

const opacity = useSharedValue(0);

2. useAnimatedStyle

Hook, który pozwala przypisać animowane wartości do stylu komponentu. Łączy się z shared value, aby dynamicznie aktualizować styl bez przeładowywania komponentu.

const animatedStyle = useAnimatedStyle(() => ({
  opacity: opacity.value,
}));
Potrzebujesz wsparcia w projektach mobilnych?
Potrzebujesz wsparcia w projektach mobilnych?
Potrzebujesz wsparcia w projektach mobilnych?
Skontaktuj się z nami!

3. Worklets

Worklety to funkcje oznaczone jako "worklet", które są uruchamiane poza głównym wątkiem JS — bezpośrednio na natywnym silniku (np. Hermes). To klucz do płynnych animacji, które nie zależą od wydajności JavaScriptu.

runOnUI(() => {
  'worklet';
  sharedValue.value = withSpring(1);
})();

4. Animatory: withTiming, withSpring, withDelay

Reanimated oferuje gotowe metody animacji wartości:

  • withTiming – animacja liniowa w czasie (np. do przezroczystości),
  • withSpring – animacja sprężynowa z fizyką (idealna do przesunięć),
  • withDelay, withSequence, withRepeat – kombinacje animacji dla złożonych efektów.

5. runOnUI i runOnJS

Te funkcje umożliwiają komunikację między natywnym wątkiem animacji a logiką JavaScript:

  • runOnUI() – uruchamia funkcję po stronie UI (np. przy starcie animacji),
  • runOnJS() – pozwala z natywnego kontekstu wywołać JS (np. callback po zakończeniu animacji).

6. useDerivedValue

Tworzy wartość zależną od innych wartości współdzielonych, automatycznie aktualizując się przy ich zmianie.​

const derived = useDerivedValue(() => {
  return sharedValue.value * 2;
});

7. useAnimatedReaction

Reaguje na zmiany wartości współdzielonych, umożliwiając wykonywanie efektów ubocznych w odpowiedzi na te zmiany.​

useAnimatedReaction(
  () => sharedValue.value,
  (newValue, oldValue) => {
    // reakcja na zmianę wartości
  }
);

8. useAnimatedGestureHandler

Integruje gesty z animacjami, umożliwiając płynne interakcje użytkownika z interfejsem.​

const gestureHandler = useAnimatedGestureHandler({
  onStart: (event, context) => {
    // gest rozpoczęty
  },
  onActive: (event, context) => {
    // gest aktywny
  },
  onEnd: (event, context) => {
    // gest zakończony
  },
});

9. interpolate i interpolateColor

Funkcje do przekształcania wartości z jednego zakresu na inny, często używane do tworzenia płynnych przejść.

const animatedStyle = useAnimatedStyle(() => {
  return {
    opacity: interpolate(sharedValue.value, [0, 1], [0.5, 1]),
  };
});

Jak zainstalować i skonfigurować React Native Reanimated?

Jest to bardzo proste i nie sprawi Ci większych problemów. Musisz poprawnie zainstalować bibliotekę i skonfigurować projekt. Poniżej znajdziesz kroki potrzebne do uruchomienia Reanimated w typowej aplikacji React Native.

1. Aby zainstalowć React Native Reanimated uruchom w terminalu:

npm install react-native-reanimated

Jeśli korzystasz z Expo, pamiętaj, że Reanimated jest już częścią SDK, ale musisz upewnić się, że masz odpowiednią wersję.

2. Następnie w pliku babel.config.js koniecznie dodaj plugin Reanimated na końcu listy pluginów:

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['react-native-reanimated/plugin'], // <-- musi być ostatni!
};

Tworzymy pierwszą animację – płynne pojawienie się i powiększenie komponentu

Skoro już wiesz jak zainstalować bibliotekę, teraz stworzymy komponent, który przy wejściu płynnie się pojawia i lekko się powiększa. Takie animacje są bardzo często używane w prawdziwych aplikacjach — np. do prezentowania kart produktów, banerów powitalnych, powiadomień czy ekranów onboardingowych.

import React, { useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
} from 'react-native-reanimated';

export default function IntroCard() {
  const opacity = useSharedValue(0);
  const scale = useSharedValue(0.8);

  useEffect(() => {
    opacity.value = withTiming(1, { duration: 800 });
    scale.value = withTiming(1, { duration: 800 });
  }, []);

  const animatedStyle = useAnimatedStyle(() => ({
    opacity: opacity.value,
    transform: [{ scale: scale.value }],
  }));

  return (
    <Animated.View style={[styles.card, animatedStyle]}>
      <Text style={styles.text}>Witaj w Reanimated 🎉</Text>
    </Animated.View>
  );
}

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#ffffff',
    padding: 20,
    margin: 20,
    borderRadius: 12,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.2,
    shadowRadius: 4,
    elevation: 5,
    alignItems: 'center',
  },
  text: {
    fontSize: 18,
    fontWeight: '600',
  },
});

Co tu sie dzieję?

Przygotowanie wartości do animacji

const opacity = useSharedValue(0);
const scale = useSharedValue(0.8);

Co to robi?
Tworzymy tzw. „shared values”, czyli specjalne zmienne, które będą się zmieniać podczas animacji — ale bez wpływu na główny wątek Reacta. opacity kontroluje przezroczystość, a scale odpowiada za rozmiar.

Następnie uruchamiamy animacje po pojawieniu się komponentu:

useEffect(() => {
  opacity.value = withTiming(1, { duration: 800 });
  scale.value = withTiming(1, { duration: 800 });
}, []);

Dlaczego tak?
Używamy withTiming, żeby płynnie zmieniać wartości w czasie. opacity rośnie od 0 do 1 (komponent staje się widoczny), a scale od 0.8 do 1 (lekki efekt „powiększenia”).

Tworzymy styl animowany:

const animatedStyle = useAnimatedStyle(() => ({
  opacity: opacity.value,
  transform: [{ scale: scale.value }],
}));

Ten styl będzie się dynamicznie aktualizował razem z naszymi shared values. Działa to jak „magia” — bez żadnego setState, bez migania, bez spowalniania aplikacji.

Zastosowanie animowanego komponentu:

<Animated.View style={[styles.card, animatedStyle]}>
  <Text style={styles.text}>Witaj w Reanimated 🎉</Text>
</Animated.View>

Używamy Animated.View, bo tylko on potrafi zrozumieć animowane style. Sam Text się nie animuje — tylko kontener, w którym się znajduje.

Reanimated vs Animated – dlaczego warto przesiąść się na nowoczesne podejście?

React Native od początku oferuje bibliotekę Animated, która pozwala tworzyć animacje w aplikacjach. Jednak wraz ze wzrostem oczekiwań użytkowników i potrzebą lepszej wydajności, wiele zespołów zaczęło szukać bardziej elastycznego rozwiązania. Tak powstał React Native Reanimated – narzędzie, które zrywa z ograniczeniami starego podejścia.

1. Wydajność
Animated działa na wątku JavaScript, co oznacza, że każda animacja przechodzi przez bridge JS-Native. W praktyce prowadzi to do opóźnień, zwłaszcza jeśli aplikacja jest mocno obciążona. Z kolei Reanimated wykonuje animacje bezpośrednio na natywnym wątku UI, co daje płynność porównywalną z aplikacjami mobilnymi pisanymi w Swift czy Kotlinie.

2. API i styl kodowania
Animated opiera się na podejściu imperatywnym — musimy stworzyć zmienną animacyjną (new Animated.Value()), zainicjować ją, a potem „ręcznie” uruchomić animację za pomocą .start(). W Reanimated pracujemy w sposób deklaratywny: używamy hooków takich jak useSharedValue czy useAnimatedStyle, co czyni kod bardziej czytelnym i logicznym, szczególnie w dużych projektach.

3. Re-renderowanie komponentów
Animated często wymaga dodatkowych trików, aby animować komponenty bez ich ciągłego przeładowywania. Reanimated nie potrzebuje re-renderów – wszystkie wartości żyją poza Reactem i są aktualizowane w tle, co wpływa pozytywnie na wydajność i stabilność.

4. Obsługa gestów
Z Animated animowanie w reakcji na gesty (np. przeciąganie palcem) bywa trudne i nieintuicyjne. W przypadku Reanimated, dzięki ścisłej integracji z react-native-gesture-handler, obsługa gestów jest naturalna i bardzo precyzyjna. Można tworzyć złożone interakcje z płynnością natywnej aplikacji.

5. Elastyczność i możliwości
Reanimated daje dostęp do zaawansowanych narzędzi jak withSpring, withSequence, withDelay, interpolate, useDerivedValue, czy runOnJS. Takiej elastyczności nie oferuje żadne inne API do animacji w React Native. W porównaniu z tym, Animated wypada dość sztywno i wymaga więcej pracy przy złożonych przypadkach użycia.

6. Wsparcie i rozwój
Animated to starsze API, obecne w React Native od dawna, ale obecnie rozwijane bardzo powoli. Reanimated to aktywny, nowoczesny projekt rozwijany przez Software Mansion, wspierany przez dużą społeczność i wykorzystywany w wielu topowych aplikacjach mobilnych.

Przykład – animacja opacity w obu podejściach:

Animated:

Animated.timing(animatedValue, {
  toValue: 1,
  duration: 500,
  useNativeDriver: true,
}).start();

Trzeba osobno tworzyć referencję (new Animated.Value(0)), dbać o start(), i martwić się o useNativeDriver.

Reanimated:

opacity.value = withTiming(1, { duration: 500 });

Najczęstsze błędy i pułapki

Reanimated to potężna biblioteka, ale jak każde narzędzie – ma swoje niuanse. Zwłaszcza na początku łatwo popełnić kilka typowych błędów, które mogą prowadzić do frustracji i nieoczywistych błędów w działaniu aplikacji. Oto lista najczęstszych problemów oraz ich przyczyn:

1. Brak pluginu w babel.config.js

Najczęstszy klasyk – zapomnienie o dodaniu "react-native-reanimated/plugin" w pliku babel.config.js. Plugin musi być ostatni na liście pluginów — inaczej kod z worklet nie zostanie przetworzony poprawnie.

plugins: ['react-native-reanimated/plugin'] // <-- ostatni!

2. Nieprawidłowe komponenty (np. View zamiast Animated.View)

Jeśli próbujesz użyć useAnimatedStyle na zwykłym View, nic się nie stanie — styl animowany musi być przypisany do komponentu z przestrzeni Animated.

Poprawnie:

<Animated.View style={animatedStyle} />

Niepoprawnie:

<View style={animatedStyle} /> // Nie zadziała!

3. Użycie setState wewnątrz worklet

Worklety działają poza Reactem, więc nie można w ich wnętrzu używać funkcji takich jak setState() czy console.log() bez użycia runOnJS. Jeśli musisz uruchomić coś w świecie JS z poziomu worklet, użyj:

runOnJS(setState)(nowaWartosc);

4. Próba animowania stylu, który nie obsługuje natywnego drivery

Nie wszystkie style mogą być animowane w Reanimated (np. zIndex, flexGrow, fontWeight — w niektórych wersjach). Zawsze warto przetestować animację krok po kroku i używać tylko wspieranych właściwości.

5. Zapominanie o value

W przypadku sharedValue, musisz odwoływać się do .value – to najczęściej pomijany szczegół przez osoby uczące się Reanimated.
Poprawnie:

opacity.value = withTiming(1); // ✅

Dlaczego React Native Reanimated to coś więcej niż tylko „ładne” animacje?

Na pierwszy rzut oka React Native Reanimated może wydawać się tylko biblioteką do upiększania interfejsów. Tymczasem jego rola w aplikacji mobilnej wykracza daleko poza samą estetykę. W nowoczesnym mobile app development – animacje nie są jedynie dodatkiem wizualnym — pełnią funkcję informacyjną, organizacyjną i emocjonalną. I to właśnie Reanimated pozwala je realizować w sposób, który jest zarówno technicznie wydajny, jak i projektowo świadomy.

Dobrze zaprojektowane animacje ułatwiają zrozumienie kontekstu aplikacji: pokazują relacje między elementami, informują o zmianie stanu, prowadzą użytkownika przez kolejne kroki i nadają rytm interakcji. Zamiast zaskakujących przejść czy nagłych zmian w układzie, użytkownik dostaje płynny, spójny flow, który wzmacnia poczucie kontroli i intuicyjności. Reanimated pozwala na bardzo precyzyjne odwzorowanie takich mikrointerakcji — bez lagów, z pełną kontrolą nad gestami i animacjami.

Z perspektywy technicznej, Reanimated to również narzędzie architektoniczne. Przeniesienie logiki animacji na natywny wątek pozwala znacząco odciążyć główny wątek JavaScript, co ma ogromne znaczenie przy rozbudowanych ekranach, asynchronicznych danych, gestach i scrollach. W aplikacjach, które muszą działać płynnie na urządzeniach średniej klasy, taka separacja logiki to wręcz warunek konieczny.

Reanimated daje też możliwość pisania interfejsu w sposób deklaratywny, spójny i łatwy do testowania. Tworzenie animacji nie wymaga już osobnego zarządzania stanem komponentu czy wymyślania workarounds. Logika zachowania komponentu może być zapisana jako czyste, deterministyczne funkcje oparte na sharedValue, useDerivedValue czy interpolate. To czyni kod prostszym, bardziej czytelnym i łatwiejszym w utrzymaniu.

Dlatego właśnie Reanimated nie powinien być traktowany wyłącznie jako biblioteka „ładnych efektów”. To pełnoprawne narzędzie inżynierskie i UX-owe, które pozwala projektować interfejsy bardziej świadomie — z naciskiem na użyteczność, wydajność i przewidywalność. W nowoczesnym świecie aplikacji mobilnych to nie tylko przewaga. To standard.

Rekomendowane źródła nauki

Jeśli chcesz lepiej zrozumieć potencjał Reanimated i pogłębić wiedzę, warto sięgnąć po kilka sprawdzonych źródeł, które pomogą Ci krok po kroku, np.: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/, https://github.com/software-mansion/react-native-reanimated

FAQ – najczęściej zadawane pytania

1. Czy mogę używać Reanimated z Expo?

Tak, Reanimated działa z Expo, ale pełne wsparcie (zwłaszcza dla gestów i niektórych funkcji natywnych) dostępne jest od SDK 46 wzwyż. Upewnij się, że masz aktualną wersję Expo i odpowiednio skonfigurowany plugin Babel.

2. Czy Reanimated działa z React Navigation?

Tak, i to bardzo dobrze. Reanimated jest wręcz zalecanym rozwiązaniem dla animowanych przejść ekranów w React Navigation. Działa bezkonfliktowo z react-native-screens i gesture-handler.

3. Jakie animacje mogę stworzyć z Reanimated?

Praktycznie każde – od prostych fade-inów i transformacji, po zaawansowane bottom sheets, interaktywne carousels, drag & drop, swipeable lists czy płynne reakcje na gesty i scroll. Reanimated daje pełną swobodę.

4. Czy Reanimated działa zarówno na Androidzie, jak i iOS?

Tak. Reanimated działa natywnie i w pełni wspiera obie platformy — iOS i Android. Co ważne, działa płynnie nawet na starszych urządzeniach i średniej klasy telefonach.

Powiązane artykuły
Zobacz wszystkie
Odkryj więcej tematów