Articles · GuidesUpdated October 2026

Animations that do not drop frames are the ones your JavaScript is not running.

An animation stops dropping frames when the UI thread runs it instead of your JavaScript, and in React Native that comes down to one flag and a short list of properties. The flag is useNativeDriver. Set it to true and the whole animation is handed to native code before it starts, so it keeps time even while your own code is busy. The list is where people come unstuck. React Native's guide says you can only animate non-layout properties this way: transform and opacity work, Flexbox and position properties do not. So a fade or a slide can be made smooth with one line, and a card that grows in height cannot.

This page is for someone who has been handed a React Native and Expo project and has to live with its animations, not for someone choosing a framework. What follows is what a dropped frame actually is and which of the two frame rates you are reading, what the native driver covers and what it refuses, which animations stutter even with the flag on, and what changes when a generator wrote the code. Every figure and every quote comes from React Native's own documentation, read on 17 October 2026.

See what the native driver covers

The short version

Two threads, one frame budget, and only one of them keeps time when your code is busy.

React Native's performance guide gives the budget in numbers. iOS and Android devices display at least 60 frames per second, which it says leaves you and the UI system at most 16.67 milliseconds to do all the work needed to generate the frame the user will see. Miss that window and you drop a frame, and the guide's own description of the result is that the UI will appear unresponsive.

The work can miss that window in two different places, which is why the in-app Perf Monitor shows two frame rates and not one. Your JavaScript thread runs your React components, your API calls and your touch handling. The native main thread draws. An Animated animation with no native driver works out every keyframe on the JavaScript thread, so anything slow in your own code lands inside the animation. With the flag on, the animation was handed over before it started, and the documentation says the JS thread can then be blocked without affecting it.

What actually drops a frame?

A dropped frame is work that did not finish in time. The device asks for a new picture at least sixty times a second, your app has at most 16.67 milliseconds to produce each one, and anything that overruns means the screen shows the old picture again. One late frame is invisible. A run of them is what people mean by janky.

The useful part is that the overrun has an address. React Native's performance guide separates the JS frame rate from the UI frame rate and treats them as different questions. On the JavaScript side, it says that if the thread is unresponsive for a frame then that counts as a dropped frame, and it works the cost out: set new state on the root component of a complex app, re-render expensive subtrees, take 200 milliseconds, and you have dropped twelve frames while any JavaScript driven animation appears to freeze. On the native side, a ScrollView keeps scrolling perfectly well while the JavaScript thread is locked up, because the scroll lives on the main thread and does not wait for your code to receive the event.

That split is also why the first measurement most people take is worthless. The guide is blunt about it: JavaScript thread performance suffers greatly in dev mode, this is unavoidable because far more work happens at runtime to give you good warnings and error messages, and you should always test performance in release builds. It adds that console.log calls can be a big bottleneck in the JavaScript thread of a bundled app, including the ones a third party library makes without telling you. So the honest order is: ship a release build to a device, watch it there, and only then start changing code. If you are already publishing to the App Store, the TestFlight build you upload is that measurement, which makes it the cheapest performance test you have.

Note that the budget belongs to the device, not to the framework. This matters if someone is weighing Flutter against React Native on smoothness. The 16.67 milliseconds is the same number in both. What differs is which thread your animation ends up on and how easy it is to get it onto the right one.

React Native, Performance, read 17 October 2026, page last updated 12 August 2026

What can the native driver animate, and what can it not?

Turning it on is one property in the animation config. React Native's animations guide says to set useNativeDriver to true when you start the animation, and notes that animations without the property default to false for legacy reasons while emitting a warning and a typechecking error in TypeScript. If you are reading a project you did not write, that default is the thing to search for: an older file with no flag at all is running its animation in JavaScript.

What it can animate is where the honest answer is narrower than the advice you will read elsewhere. The guide states that not everything you can do with Animated is currently supported by the native driver, and that the main limitation is that you can only animate non-layout properties: things like transform and opacity will work, but Flexbox and position properties will not. Fades, slides, scales and rotations are on the right side of that line. Height, width, padding, margin, top, left, right and bottom are not.

Three more conditions come with it, and each one has a tell. An animated value is only compatible with one driver, so if one animation on a value uses the native driver then every animation on that value has to. When you drive an animation from a gesture with Animated.event, the native driver only works with direct events and not bubbling events, which the guide spells out as working with a ScrollView's onScroll and not working with PanResponder. And a running animation can stop a VirtualizedList rendering more rows, so a looping animation over a scrolling list wants isInteraction: false in its config. Animated exports six animatable component types, View, Text, Image, ScrollView, FlatList and SectionList, and anything else has to be wrapped with createAnimatedComponent before it can be animated at all.

One thing to be clear about, because it is the kind of claim that gets repeated wrongly: React Native does not publish a complete list of the properties the native driver supports. I read the animations guide and the Animated API reference on 17 October 2026 and neither contains an exhaustive table. Transform and opacity are named as working, Flexbox and position properties are named as not working, and everything else is unstated. Treat the unstated cases as something to verify in a release build rather than something to argue about.

React Native, Animations, read 17 October 2026, page last updated 14 October 2026

Check yours

Will the native driver take it off your JavaScript thread

Tick what your screen animates. Every verdict below comes from React Native's own animations guide and performance guide.

Nothing ticked. A screen with no animation on it cannot drop a frame to one, which is its own kind of answer.

Which animations stutter even with the flag on?

The flag moves the animation to the other thread. It does not make that thread free. React Native's performance guide lists the native side failures separately, and the one that catches most people is moving a view across the screen at all. It says this is especially true on Android when you have text with a transparent background sitting on top of an image, or any other situation where alpha compositing would be needed to redraw the view on every frame. Its suggested fix is to rasterise the view, with a caution attached that overusing that can send your memory usage through the roof.

Images are the second one. The guide says that on iOS, every time you adjust the width or height of an Image component it is re-cropped and scaled from the original image, which can be very expensive for large images, and it says to animate a scale transform instead. That is the same advice as the layout property rule, arrived at from a different direction, which is a good sign it is the real constraint. A third one is lists: if a FlatList renders slowly the guide points at implementing getItemLayout so it can skip measuring rendered items.

There is also a tap that feels slow but is not an animation problem at all. When a touchable is unresponsive, the guide explains that the JavaScript thread is busy and cannot process the raw touch events the main thread sent over, and the suggested fix is to wrap the work inside your onPress handler in requestAnimationFrame so the press feedback gets its frame first. If your complaint is that the app feels heavy rather than that something visibly judders, start there.

Before any of this, check what your project actually animates with. If package.json lists react-native-reanimated, which Expo documents as a third party library you add with npx expo install, then its animations are not the Animated API and useNativeDriver is not the switch that governs them. If it lists nothing, you are on Animated and LayoutAnimation, and the rules above are the whole picture. Which version of the SDK you are on changes which libraries came with the project, so the Expo SDK release notes are worth a read before you add another dependency to fix a frame you have not measured.

The two frame rates, and what each one tells you

What you are looking atJS frame rate, JavaScript threadUI frame rate, main threadDoes the native driver change it
What it measuresWhether your JavaScript finished its work inside the frameWhether the native side managed to draw the frameNeither, it moves the work from one column to the other
Blocking it freezes an Animated animationYesNoYes
A heavy re-render of the root component costs youTwelve dropped frames in the documented exampleNothing on its ownYes
A ScrollView keeps scrolling while it is stuckNoYesNo
Compositing a translucent view over an image hurts itNoYesNo
Numbers from a development build can be trustedNoCloser, but confirm in a release buildNo

Both numbers come from the same place, the Perf Monitor in the in-app dev menu. Read them together. A low JS frame rate with a healthy UI frame rate is your code; the reverse is drawing.

What does it change that a generator wrote the animation?

Nothing about the frame budget. The device asks for its sixty frames whoever typed the file, and the two threads are React Native's, not the author's. What changes is your position: you are reading a decision rather than making one, so the work is an audit instead of a design. Search the project for useNativeDriver and see which animations set it. Search for height, width, top and left inside animated styles, because those are the ones that cannot move off your JavaScript thread no matter what flag you add. Check whether a value is shared between a native driven animation and a JavaScript one, which the documentation rules out.

Newly generates a real React Native and Expo project that you own. It uploads iOS builds to TestFlight, publishes to Google Play internal testing and builds a standalone Android APK, and it has two way GitHub sync so the code is readable in your own editor rather than only inside a tool. It does not submit to the App Store for you, because Apple requires the owner of the app's content to submit it. It is $25 a month, with no free tier. The relevant part here is the first one: you can open the file, read the animation config and change one property, which is the entire fix for most of what this page describes. If the ownership question is the one you actually have, read who owns AI generated code first, because everything on this page assumes you can edit what you were handed.

The honest limit is worth saying plainly. A generator can set useNativeDriver to true on every animation it writes, and that is a good default. It cannot decide that your product needs a card to grow in height, and height is a layout property. When the design and the thread disagree, somebody has to choose, and that somebody is you. Those are the frames worth spending an afternoon on. The rest of the time, the fix is a flag you can add in a minute and a release build to prove it worked.

Questions people ask about dropped frames

It hands the whole animation to native code before the animation starts. React Native's animations guide puts it this way: everything about the animation is sent to native before it begins, so native code can run it on the UI thread without going through the bridge on every frame, and once it has started the JS thread can be blocked without affecting the animation. That last clause is the whole point. Without the flag, the Animated API works out each keyframe on demand on the JavaScript thread, so anything slow in your own code lands in the animation. Read 17 October 2026.

Describe the app, then read what it animates

You get the project, so you get the animation config too. Open it, check the flag, and judge the result on a build you shipped to a phone rather than one running in dev mode.

Start building