+1 (415) 943-4271

Reanimated 4 in Practice: CSS Animations, the Worklet Rules, and Migrating from v3

Animation is where React Native apps either feel native or feel like a website in a WebView. For years the answer has been Reanimated, and for years that meant learning worklets, shared values, and the mental model of code that runs on the UI thread instead of the JS thread.

Reanimated 4 changes the entry cost. Alongside the worklet APIs you already know, it ships a declarative, CSS-style animation and transition API that covers most of what product teams actually build: a button that pulses, a card that fades in, a chevron that rotates, a row that slides out. You write it as style props, and it runs on the UI thread without you writing a single worklet.

It also comes with one hard requirement that decides your upgrade timeline: Reanimated 4 runs only on the New Architecture.

The requirement you have to clear first

Reanimated 3 supported both the old bridge-based architecture and Fabric. Reanimated 4 dropped old architecture support entirely. If your app still has newArchEnabled=false in android/gradle.properties or the equivalent iOS flag, Reanimated 4 is not an option yet — you will stay on the 3.x line until the migration lands.

That is not a reason to panic. React Native has been defaulting to the New Architecture for several releases, and the legacy renderer is on its way out of the core. If you are still on the old architecture, the ordering is simple:

  1. Migrate to the New Architecture (dependency audit, Turbo Module conversion, Fabric rendering fixes).
  2. Ship that, let it bake, watch your crash rate.
  3. Then upgrade Reanimated.

Doing both in one release turns every animation glitch into an argument about which change caused it. We have written up the first step separately in our New Architecture migration checklist; treat Reanimated 4 as the reward at the end of it, not part of it.

Two animation models, one library

After the upgrade you have two ways to animate, and the useful skill is knowing which one a given screen wants.

1. CSS animations and transitions (new in v4)

Declarative. You describe the target style and the timing, and the library handles the rest. There are no shared values, no worklets, no useAnimatedStyle.

A transition animates whenever a style property changes:

import Animated from 'react-native-reanimated';

function ExpandableCard({ expanded }) {
  return (
    <Animated.View
      style={{
        height: expanded ? 220 : 72,
        transitionProperty: 'height',
        transitionDuration: 250,
        transitionTimingFunction: 'ease-in-out',
      }}
    />
  );
}

A keyframe animation runs on its own, on a loop if you want it to:

const pulse = {
  from: { opacity: 1, transform: [{ scale: 1 }] },
  to: { opacity: 0.6, transform: [{ scale: 1.06 }] },
};

<Animated.View
  style={{
    animationName: pulse,
    animationDuration: '1200ms',
    animationIterationCount: 'infinite',
    animationDirection: 'alternate',
  }}
/>

If you have written CSS in the last decade this needs no explanation, which is exactly the point. A React web developer joining a React Native team can ship a correct, UI-thread animation on day one instead of week three.

2. Worklets and shared values (unchanged from v3)

Imperative and gesture-driven. This is still the right tool whenever the animation is driven by something continuous — a finger, a scroll offset, a physics-y spring that has to hand off to a gesture.

const offset = useSharedValue(0);

const pan = Gesture.Pan()
  .onChange((e) => {
    offset.value += e.changeX;
  })
  .onEnd((e) => {
    offset.value = withSpring(0, { velocity: e.velocityX });
  });

const styles = useAnimatedStyle(() => ({
  transform: [{ translateX: offset.value }],
}));

Nothing here changed conceptually in v4, so your existing gesture code is mostly a straight port.

Choosing between them

SituationUse
State-driven enter/exit, expand/collapse, colour or opacity changeCSS transition
Looping attention animation, skeleton shimmer, spinnerCSS animation
Anything attached to a gesture or scroll positionWorklets + shared values
Animation that must read layout values at runtime and branchWorklets
Handoff from a gesture into a decaying springWorklets

A good rule: if you can express the animation as "when this boolean flips, move from A to B", use the declarative API. If the animation needs to know where the finger is right now, use worklets.

What the worklets runtime change means for you

In v4 the worklet machinery was extracted into its own package (react-native-worklets). For most app code this is an implementation detail — you keep importing from react-native-reanimated. It matters in two places:

  • Peer dependencies. Libraries that lean on worklets directly (gesture-heavy UI kits, camera frame processors, skia-based renderers) need versions that know about the split. Check them before you upgrade, not after.
  • Multiple runtimes. Running heavy JS off the main JS thread via a dedicated worklet runtime is now a first-class thing rather than a Reanimated trick. If you have been doing image processing or parsing on the UI runtime because it was convenient, revisit it — that work now belongs on its own runtime, not on the thread that draws frames.

Migrating a production app from v3 to v4

A path that has held up well for us:

1. Confirm New Architecture is on and stable in production. Not on a branch. In production, with a week or two of crash data.

2. Audit animation-adjacent dependencies. react-native-gesture-handler, bottom sheet libraries, carousel libraries, Skia, camera, chart libraries. Each one needs a version that supports Reanimated 4. This audit is where upgrades die; do it first and be honest about anything unmaintained.

3. Upgrade in a branch and run the app, do not just run the tests. Animation regressions almost never fail a unit test. They show up as a sheet that snaps instead of slides, or a shimmer that stops after one loop.

4. Do not rewrite working worklet code. The temptation after reading the CSS API is to convert everything. Don't. Convert an animation only when you are already editing that screen, and only when the declarative version is genuinely simpler.

5. Watch layout animations specifically. Entering/exiting/layout transitions are the area where behaviour differences between v3 and v4 are most visible, especially in lists and on Android. Test list insertion and deletion by hand on a low-end Android device.

6. Keep a rollback lever. Version-pin Reanimated, ship behind a staged rollout, and be prepared to revert one dependency rather than a whole release train.

Performance notes that still apply

The new API does not repeal the old rules.

  • Animate transform and opacity when you can. Animating height, width, or margin forces layout work per frame. Sometimes it is unavoidable — an expanding card is the classic case — but reach for scale and translate first.
  • Do not animate hundreds of nodes at once. A shimmer on every row of a long list is a frame-rate problem regardless of which API drew it. Animate the container, or animate only what is on screen.
  • Profile on the worst device you support. Everything is 60fps on a current-generation iPhone. The truth lives on a three-year-old mid-range Android.
  • Remember the JS thread can still stall you. UI-thread animations survive a busy JS thread, which is the whole point — but if a heavy useEffect blocks JS while a gesture is trying to hand off to a spring, users still feel it.

Where teams get stuck

In the upgrades we have run, the friction is rarely Reanimated itself. It is a chain of dependencies where one animation-heavy library has not shipped a v4-compatible release, and the team has to decide whether to fork it, replace it, or wait. Making that call early — before you have half-migrated screens — is the difference between a one-sprint upgrade and a quarter-long one.

If your app is still on the old architecture and you are trying to sequence New Architecture, Reanimated 4, and a React Native version bump without freezing feature work, get in touch. Planning that order is most of the job; the code changes are usually the easy part.