React Native Reanimated 4 em 2026: Guia Completo de Animações CSS, Worklets e Layout Transitions

Guia prático do React Native Reanimated 4 em 2026: nova API CSS, worklets, shared element transitions e migração indolor do Reanimated 3, com código pronto para copiar.

Reanimated 4: Guia de Animações 2026

Atualizado: 10 de Julho de 2026

O React Native Reanimated 4 é a versão da biblioteca de animações que introduz uma API declarativa inspirada em CSS, roda exclusivamente sobre a Nova Arquitetura (Fabric) e mantém compatibilidade retroativa com os worklets do Reanimated 3, permitindo animar propriedades como transform, opacity e backgroundColor diretamente na thread de UI a 120 fps. Quem vem do React Web sente a curva encurtar: você escreve animationName, animationDuration e @keyframes como faria no navegador, só que o runtime continua sendo o worklet nativo. Esse guia cobre a nova API, migração do Reanimated 3, shared element transitions e as armadilhas que descobri portando um app de produção no mês passado.

  • Reanimated 4 exige a Nova Arquitetura do React Native (Fabric habilitado) e React Native 0.78 ou superior. Não há mais fallback para a Paper Renderer.
  • A nova API CSS aceita animationName, animationDuration, animationTimingFunction e transitionProperty diretamente em StyleSheet.create, sem useSharedValue.
  • Worklets (useSharedValue, useAnimatedStyle, runOnJS) continuam funcionando e são recomendados para gestos e animações imperativas ligadas a interação contínua.
  • Shared Element Transitions saíram do estado experimental e integram com Expo Router v7 via sharedTransitionTag.
  • Layout Animations (entering, exiting, layout) ganharam presets configuráveis e uma LayoutAnimationConfig por subtree.
  • A migração do Reanimated 3 costuma ser incremental. Worklets antigos rodam sem alteração, mas createAnimatedComponent foi realocado para Animated.

O que mudou no Reanimated 4

Se você trabalhou com Reanimated 2 ou 3, a mudança conceitual do 4 é grande. A biblioteca deixa de ser puramente imperativa (baseada em useSharedValue) e passa a oferecer uma API declarativa inspirada em CSS. A equipe do Software Mansion documentou a decisão nas notas de release do Reanimated 4: eles queriam reduzir o boilerplate de casos simples (fade-in, translate, hover) sem quebrar quem já dependia de worklets.

Na prática, três coisas mudam no dia a dia. Primeiro, a dependência de peer package react-native-worklets foi extraída. O Reanimated 4 depende dela para o runtime UI, o que permite outras bibliotecas (como Skia) compartilhar o mesmo mecanismo. Segundo, a Nova Arquitetura é obrigatória; a versão 4 não roda em apps que ainda usam Paper Renderer. Terceiro, chegou o Animated.createAnimatedComponent unificado, que aceita componentes Fabric nativamente sem shim manual, algo que quem já migrou para a Nova Arquitetura do React Native conhece de perto.

Honestamente, quem vem do React Web percebe rápido que animationName mapeia para @keyframes, transitionProperty mapeia para transições CSS e animationIterationCount funciona igual. A diferença é que o motor não é o navegador: é um worklet nativo executando no thread de UI, então você não paga custo de reconciliação por frame.

Requisitos e instalação com Expo SDK 54+

Reanimated 4 exige React Native 0.78 ou posterior. Se você está no Expo, a versão gerenciada é a SDK 54 (mainline em julho de 2026); a SDK 55, em beta, já traz React Native 0.79. A Nova Arquitetura precisa estar ligada. Em Expo SDK 54+ ela é o padrão, mas vale confirmar em app.json.

// app.json (Expo)
{
  "expo": {
    "name": "MeuApp",
    "newArchEnabled": true,
    "plugins": ["expo-router"]
  }
}

A instalação em projetos Expo é direta:

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

# React Native puro (bare workflow)
npm install react-native-reanimated@^4.0.0 react-native-worklets@^0.5.0
cd ios && pod install

O plugin do Babel continua obrigatório. Sem ele, worklets simplesmente não são compilados. No babel.config.js, garanta que react-native-worklets/plugin apareça por último na lista de plugins (a ordem importa; explico o motivo logo abaixo).

// babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      // outros plugins primeiro...
      'react-native-worklets/plugin', // sempre por último
    ],
  };
};

Animações CSS: a nova API declarativa

A cara nova do Reanimated 4 são propriedades de animação diretamente no style. Não há mais useSharedValue nem useAnimatedStyle para fades ou entradas simples. Você declara o que quer e o runtime cuida do frame loop.

import { StyleSheet, View, Text } from 'react-native';

export default function Toast({ visible, message }) {
  return (
    <View style={[styles.toast, visible && styles.toastVisible]}>
      <Text style={styles.text}>{message}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  toast: {
    opacity: 0,
    transform: [{ translateY: -20 }],
    transitionProperty: ['opacity', 'transform'],
    transitionDuration: '250ms',
    transitionTimingFunction: 'ease-out',
  },
  toastVisible: {
    opacity: 1,
    transform: [{ translateY: 0 }],
  },
  text: { color: '#fff', fontSize: 14 },
});

Repare que este componente é um View comum, não precisa ser Animated.View. O Reanimated 4 detecta as propriedades transition* e injeta o driver de animação automaticamente. Quem vem do React Web já reconheceu o padrão: transitionProperty lista as propriedades, transitionDuration aceita '250ms' ou número em milissegundos.

Keyframes com animationName

Para animações de loop ou entradas complexas, use @keyframes declarados via a função keyframes e referenciados por nome:

import { StyleSheet } from 'react-native';
import { keyframes } from 'react-native-reanimated';

const pulse = keyframes({
  from: { transform: [{ scale: 1 }], opacity: 1 },
  '50%': { transform: [{ scale: 1.08 }], opacity: 0.85 },
  to: { transform: [{ scale: 1 }], opacity: 1 },
});

const styles = StyleSheet.create({
  badge: {
    animationName: pulse,
    animationDuration: '1200ms',
    animationIterationCount: 'infinite',
    animationTimingFunction: 'ease-in-out',
  },
});

Vale destacar que animationIterationCount aceita 'infinite' ou um número, exatamente como no CSS. A propriedade animationDirection suporta 'alternate', o que é útil para efeitos de respirar sem duplicar keyframes.

Worklets ainda servem? Quando usar cada abordagem

Sim, e não são deprecados. A API imperativa via worklets continua sendo a forma correta quando a animação depende de interação contínua do usuário, como gestos, scroll ou entradas de sensores. O motivo é conceitual: CSS animations rodam sobre um contrato temporal (começam, terminam, repetem), enquanto worklets reagem a valores compartilhados que podem ser atualizados a qualquer momento pela thread de UI. Para o modelo mental dos worklets em si, a documentação oficial do react-native-worklets é a fonte mais atualizada.

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

export default function Draggable() {
  const tx = useSharedValue(0);
  const ty = useSharedValue(0);

  const pan = Gesture.Pan()
    .onChange((e) => {
      tx.value += e.changeX;
      ty.value += e.changeY;
    })
    .onEnd(() => {
      tx.value = withSpring(0);
      ty.value = withSpring(0);
    });

  const style = useAnimatedStyle(() => ({
    transform: [{ translateX: tx.value }, { translateY: ty.value }],
  }));

  return (
    <GestureDetector gesture={pan}>
      <Animated.View style={[styles.card, style]} />
    </GestureDetector>
  );
}

A regra prática que sigo é simples. Se a animação tem estado discreto (visível/invisível, on/off, expandido/colapsado), use CSS transitions. Se ela é dirigida por um valor contínuo (dedo, scroll, tilt), use worklets. Em nenhum momento você precisa escolher globalmente; misturar as duas abordagens no mesmo componente é suportado, e no meu último projeto acabei fazendo isso o tempo todo.

Shared Element Transitions com Expo Router

Shared Element Transitions saíram do rótulo experimental no Reanimated 4 e agora se integram nativamente com o Expo Router v7. A ideia é a mesma do Framer Motion na web (layoutId): você marca um elemento em duas telas com o mesmo sharedTransitionTag e o runtime interpola tamanho, posição e opacidade entre as duas rotas.

// app/index.tsx — lista
import { Link } from 'expo-router';
import Animated from 'react-native-reanimated';

export default function List() {
  return items.map((item) => (
    <Link key={item.id} href={`/product/${item.id}`}>
      <Animated.Image
        source={{ uri: item.image }}
        sharedTransitionTag={`product-${item.id}`}
        style={{ width: 80, height: 80 }}
      />
    </Link>
  ));
}

// app/product/[id].tsx — detalhe
import Animated from 'react-native-reanimated';

export default function Product({ item }) {
  return (
    <Animated.Image
      source={{ uri: item.image }}
      sharedTransitionTag={`product-${item.id}`}
      style={{ width: '100%', aspectRatio: 1 }}
    />
  );
}

Note que a sharedTransitionTag precisa ser única no par de rotas. Se você renderiza uma lista, inclua o id do item; usar apenas "product" quebra a interpolação porque o runtime não sabe qual imagem corresponde a qual. Bati nesse bug exato ao subir um app para produção, e a correção foi de duas linhas. Para transições mais complexas (por exemplo, ajustar borderRadius durante a interpolação), passe uma SharedTransition customizada via prop.

Se você ainda está resolvendo o roteamento base, o guia sobre rotas protegidas com Stack.Protected no Expo Router v7 explica como estruturar navegação antes de camadas de animação.

Layout Animations: entering, exiting e layout

Layout Animations continuam sendo a maneira mais barata de dar personalidade a listas e drawers. No Reanimated 4, o conjunto de presets foi ampliado e a configuração ficou composicional. Você encadeia duration, delay, easing e springify no mesmo builder.

import Animated, {
  FadeInDown,
  FadeOutUp,
  LinearTransition,
} from 'react-native-reanimated';

export default function TaskList({ tasks }) {
  return tasks.map((t) => (
    <Animated.View
      key={t.id}
      entering={FadeInDown.springify().damping(14)}
      exiting={FadeOutUp.duration(180)}
      layout={LinearTransition.springify()}
      style={styles.row}
    >
      <Text>{t.title}</Text>
    </Animated.View>
  ));
}

O prop layout é frequentemente esquecido, mas é ele que anima as mudanças de posição quando outro item é removido. Sem ele, as linhas restantes pulam abruptamente para preencher o espaço. Combinar entering, exiting e layout cobre 90% das animações de lista sem precisar tocar em worklets.

Para casos onde você quer desligar layout animations em um subtree (por exemplo, dentro de um Modal cheio de re-renders), envolva com LayoutAnimationConfig:

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

<LayoutAnimationConfig skipEntering skipExiting>
  <HeavyModalContent />
</LayoutAnimationConfig>

Se sua lista tem milhares de itens, animar cada linha custa caro mesmo com o runtime na thread de UI. Nesses casos, considere as recomendações do artigo comparativo entre FlatList, FlashList v2 e LegendList para listas no React Native. Recycler-based lists trabalham melhor com layout animations desligados em favor de transições internas.

Como migrar do Reanimated 3 para o 4

A migração costuma ser menos dolorosa do que a versão 2→3 porque os worklets sobrevivem. A checklist que uso ao portar apps:

  1. Confirme a Nova Arquitetura. Sem newArchEnabled: true, o app crasha no primeiro Animated.View. Rode npx expo prebuild --clean depois de trocar.
  2. Instale react-native-worklets como dependência direta. No Reanimated 3 ela era transitiva; no 4 você precisa listar no package.json.
  3. Substitua o plugin Babel. Onde estava react-native-reanimated/plugin, use react-native-worklets/plugin. Mantenha por último.
  4. Revise createAnimatedComponent. Componentes de terceiros que já são Fabric-native não precisam mais do wrapper; se aparecerem warnings de ref duplicada, remova o createAnimatedComponent.
  5. Teste o app com o Metro em modo --reset-cache. O plugin do worklets deixa marca no cache; sem resetar, worklets antigos podem ficar meio compilados (bati nessa há duas semanas e perdi uma tarde inteira até resetar).

A maior parte do código imperativo (useSharedValue, useAnimatedStyle, withTiming, withSpring) continua idêntica. O que muda é que agora você tem a opção de reescrever componentes simples usando a API CSS e ganhar legibilidade.

Performance e boas práticas em 2026

Reanimated 4 herda o runtime UI-thread do 3, então continua sendo raro ver drop de frame se você seguir as regras. Alguns pontos que valem lembrar:

  • Evite alocar objetos dentro de worklets. Objetos criados no worklet são copiados a cada frame; declare-os fora e mute campos por referência quando possível.
  • Não misture setState com valores compartilhados na hot path. Use runOnJS apenas em eventos discretos (fim de gesto, tap), nunca em cada frame de onChange.
  • Prefira transform a top/left. Transformações são aceleradas nativamente; propriedades de layout forçam relayout até no Fabric.
  • Habilite o novo perfilador do React Native DevTools. Ele mostra tempo gasto no worklet runtime separado da JS thread, o que ajuda a caçar animações que estão travando por causa de trabalho no JS.
  • Cuidado com o thread de UI em Android low-end. Em dispositivos com GPU fraca, animationTimingFunction: 'linear' pode parecer mais fluido que curvas de spring caras. Meça com o perfilador antes de otimizar por intuição.

Para animações que envolvem gráficos complexos (blur, shaders, path animation), o Reanimated 4 combina muito bem com o Skia. O pipeline funciona sobre o mesmo worklet runtime. O guia sobre gráficos GPU com Skia no React Native em 2026 mostra como compartilhar SharedValues entre as duas bibliotecas.

Perguntas Frequentes

O Reanimated 4 funciona sem a Nova Arquitetura?

Não. A partir da versão 4.0, a Nova Arquitetura (Fabric renderer) é requisito obrigatório. Se você ainda depende do Paper renderer, mantenha o Reanimated 3.x até completar a migração de arquitetura.

Preciso reescrever meus worklets ao migrar para o Reanimated 4?

Não. Worklets escritos com useSharedValue, useAnimatedStyle e withTiming continuam funcionando sem alterações. A única mudança obrigatória é adicionar react-native-worklets como dependência direta e usar seu plugin Babel no lugar do antigo.

Qual a diferença entre animações CSS e worklets no Reanimated 4?

Animações CSS são declarativas e ideais para estados discretos (mostrar/esconder, hover, pulse). Worklets são imperativos e melhores para animações dirigidas por interação contínua (gestos, scroll). Ambas rodam na thread de UI e podem coexistir no mesmo componente.

Shared Element Transitions do Reanimated 4 funcionam com React Navigation?

Sim, funcionam tanto com Expo Router v7 quanto com React Navigation 7. A implementação depende de o navigator suportar Fabric. Stack e Native Stack são compatíveis; alguns navigators legados podem exigir atualização.

Como testar animações do Reanimated 4 com Jest?

Use o mock oficial react-native-reanimated/mock no seu jest.setup.js. Para testes de interação, avance o timer com jest.advanceTimersByTime após disparar a animação. A versão 4 respeita fake timers para CSS animations e worklets.

Anita Iyer
Sobre o Autor Anita Iyer

Cross-platform mobile developer who came to RN from web. Bridges the two worlds and explains the seams.