Why Is My AI-Generated React Native App So Slow? (Fixes)
Most slow AI-built React Native apps are not slow because of React Native. They are slow because of five habits the model repeats.
TL;DR
An AI-generated React Native app usually feels slow for five fixable reasons: you are testing a development build, console.log and heavy work are blocking the JavaScript thread, long lists render as ScrollView or unconfigured FlatList, whole screens re-render on every keystroke, and images are oversized. Measure in a release build, move animation to the native driver or Reanimated, switch big lists to FlashList, and memoize what the profiler flags. Starting screens from a VP0 design keeps component trees shallow from the first prompt.
An AI-generated React Native app is almost never slow because of React Native. It is slow because the model repeats a handful of habits: you are testing a development build that is supposed to be slow, console.log calls and heavy work are blocking the JavaScript thread, long lists are rendered all at once, whole screens re-render on every keystroke, and images are downloaded at many times their display size. Each has a known fix, and most take minutes once you have measured which one you have. Prevention helps too: starting screens from a free VP0 design gives the agent a clear, shallow structure to reproduce, so you are not debugging a twelve-level tree of wrapper views later. Here is the diagnosis order that finds the problem fastest.
First, make sure you are measuring the right build
The most common “slow app” is not slow at all; it is a development build. The React Native team is explicit that JavaScript thread performance suffers greatly in dev mode, because the runtime is doing extra work to give you warnings and helpful errors, and it tells you to always test performance in release builds. Expo Go and a dev client both run in development mode by default, so a list that stutters there may be perfectly smooth in the build your users install.
Before changing any code, build a release version and put it on a real device, ideally an older, cheaper phone, since that is where your slowest users are. On Expo, a preview or production profile build via EAS gives you exactly that; on a bare project, run the release configuration. If the lag disappears, you have saved yourself a day. If it remains, you now have an honest baseline, and every fix below can be judged against it. The pre-launch test pass covers the rest of that real-device routine.
Understand the two threads, and why 16.67ms matters
React Native renders on a native main thread while your app logic runs on a JavaScript thread. Phones display at least 60 frames per second, which the documentation translates into a hard budget: at most 16.67ms to produce each frame. If the JavaScript thread misses that budget, touches feel late, navigation transitions stutter, and anything animated through JavaScript jerks.
The developer menu’s Perf Monitor shows both frame rates separately, and that split is your map. A dropping JS frame rate with a steady UI frame rate means your JavaScript is doing too much: logging, parsing, re-rendering, or computing in the middle of an interaction. A dropping UI frame rate points at the native side: too many views, expensive shadows and transparency, or giant images being decoded. Generated code tends to fail on the JS side, which is also the side easiest to fix.
| Symptom | Likely cause | First fix to try |
|---|---|---|
| Everything feels sluggish in Expo Go | Development mode overhead | Measure a release build on a real device |
| Typing in a field lags | Whole screen re-renders per keystroke | Move input state down, memoize siblings |
| Long list stutters or shows blanks | ScrollView or untuned FlatList | FlashList, or tune FlatList props |
| Animations jerk during loading | Animation driven on the JS thread | Native driver or Reanimated |
| Scrolling a photo grid drops frames | Full-size images decoded for thumbnails | Request sized images, use a caching image component |
| App is slow only after navigating a while | Listeners or intervals never cleaned up | Return cleanup functions from effects |
Find the JavaScript thread blockers
Start with the embarrassing one. Agents love logging, and generated code often ships with console.log inside render functions, list items, and network handlers. The React Native docs warn that in a bundled app these statements can cause a big bottleneck in the JavaScript thread, including logging from debugging libraries. Search the project for them, delete what you do not need, and strip the rest from production builds with the Babel plugin the docs recommend.
Next, look for work that happens during interactions. Parsing a large JSON response, sorting thousands of items, or formatting every date in a list on each render all compete with the frame budget. Move heavy computation out of render, compute it once when data arrives, and cache the result. For work that can wait until an animation or transition finishes, defer it rather than racing it.
Animations deserve special attention. The Animated API calculates each frame on the JavaScript thread unless you set useNativeDriver: true, which hands the animation to the native side so it keeps running smoothly even while JavaScript is busy. For gestures and anything more ambitious, Reanimated runs animation logic on the UI thread. Generated code often animates opacity and position through plain state updates; converting those is one of the most visible improvements you can make.
Fix the lists, where most AI apps lose their frames
Lists are where generated apps suffer most, because the model reaches for the simplest thing that renders: a ScrollView with .map() over the whole array. That mounts every row at once, which is fine for ten rows and painful for five hundred. The first fix is to use a virtualized list. The second is to configure it. React Native’s FlatList optimization guide lists the knobs: windowSize defaults to 21, meaning ten viewports above and ten below are kept mounted, and lowering it reduces memory and work at the cost of occasional blank space; getItemLayout removes async layout measurement when rows share a height, which the guide calls a very desirable optimization; and keeping row components light and stable matters as much as any prop.
For long or complex feeds, the bigger win is often switching to FlashList, which keeps props close to FlatList’s but recycles cell components under the hood instead of creating new ones as you scroll. In practice that means fewer blank cells and far less JavaScript work on fast scrolls. It is a small migration and a good task to hand the agent with a precise instruction.
Whichever list you use, give rows a stable keyExtractor, avoid inline arrow functions and inline style objects in renderItem when the profiler shows rows re-rendering, and wrap the row component in React.memo so unchanged rows are skipped. A feed built from a well-structured design with one clear row component is much easier to optimize than one where every row is a bespoke nest of views.
Stop the re-render cascade
The quietest performance killer in generated code is state held too high. A typical agent-written form keeps every field’s value in the screen component, so each keystroke re-renders the whole screen: the header, the image, the list below, all of it. On a fast phone you may not notice; on an older one, typing feels like wading.
The fix is structural rather than clever. Keep state as close as possible to where it is used, so a text input owns its own value until submit. Split large screens into components with clear props, and use React.memo on children that receive the same props most of the time, together with useCallback for functions you pass down, so memoization is not defeated by a new function identity on each render. Do not memoize everything by reflex; React’s own documentation notes that memo is a performance optimization, not a guarantee, and blanket use adds noise. Profile, find the components that render far more than they should, and fix those.
Global state is the other cascade. A single context that holds the user, the cart, the theme, and the current screen re-renders every consumer when any of them changes. Splitting it into smaller contexts, or using a store with selectors, stops a cart update from repainting the settings screen.
Images, memory, and the leaks that build up
Images are the native-side culprit. Agents often request the original photo and let the view scale it down, so a 4,000-pixel photo is decoded to fill a 90-pixel thumbnail, roughly 44x more pixels in each direction than the screen can show. Ask your backend or image CDN for sized variants, and use a caching image component such as expo-image so the same picture is not downloaded and decoded again on every scroll. The FlatList guide makes the same point: every image in a list is a new instance, and the faster it loads, the sooner the JavaScript thread is free again.
Leaks explain the app that is fine at launch and sluggish after ten minutes. Generated effects frequently subscribe to listeners, start intervals, or open sockets without returning a cleanup function, so every visit to a screen adds another one. Search useEffect calls for subscriptions and timers and make sure each returns its cleanup. It is tedious work, and it is exactly the kind of mechanical audit an agent does well once you ask for it explicitly.
Finally, confirm the engine. Hermes is used by default by React Native and improves start-up time, memory use, and app size, so if an older template or a copied config has switched it off, turning it back on is a free improvement. If you are weighing frameworks because of speed, the React Native versus Capacitor comparison explains where each one genuinely wins.
Key takeaways: speeding up an AI-built React Native app
Measure before changing anything: a release build on an older real phone, with the Perf Monitor telling you whether JavaScript or native rendering is dropping frames. Then work down the usual suspects in order: delete logging from hot paths, move animation to the native driver or Reanimated, virtualize and tune long lists or switch them to FlashList, push state down and memoize what the profiler flags, and serve images at the size they are shown. Give the agent the evidence and a precise instruction for each, not a vague request to make it faster. And prevent the next round by starting screens from a free VP0 design, so the component structure the agent writes is shallow and list-friendly from the first prompt.
Frequently asked questions
Why is my AI-generated React Native app so slow? Usually one of five things: you are judging a development build, the JavaScript thread is blocked by logging or heavy work, a long list is rendered with ScrollView or an unconfigured FlatList, screens re-render far more than they should, or images are far larger than they are displayed. Fix those in order, and start new screens from a VP0 design so the agent generates shallow, list-friendly component trees from the beginning.
Is React Native itself too slow for a real app? No. React Native renders native views, and its JavaScript engine, Hermes, is on by default. Plenty of large apps ship on it at a smooth frame rate. When an app is slow, the cause is almost always the code on top of the framework, which is good news, because code can be fixed.
How do I know whether the lag is JavaScript or native? Turn on the Perf Monitor from the developer menu. It shows the JS frame rate and the UI frame rate separately. If the JS number drops while the UI number holds, your JavaScript is doing too much work; if the UI number drops, look at heavy views, shadows, oversized images, or layout.
Should I replace FlatList with FlashList? For long or complex lists, it is often the single biggest win, because FlashList recycles cell components instead of mounting new ones, and its props are close to FlatList’s. For a short list of a dozen rows, a plain map or a tuned FlatList is fine.
Can I ask the AI to fix the performance itself? Yes, if you give it evidence. Paste the profiler output or name the slow screen and the measured frame drop, and ask for specific changes: memoize this row, move this animation to the native driver, switch this list to FlashList. A vague “make it faster” tends to produce churn without a measurable gain.
Other questions VP0 users ask
Why is my AI-generated React Native app so slow?
Usually one of five things: you are judging a development build, the JavaScript thread is blocked by logging or heavy work, a long list is rendered with ScrollView or an unconfigured FlatList, screens re-render far more than they should, or images are far larger than they are displayed. Fix those in order, and start new screens from a VP0 design so the agent generates shallow, list-friendly component trees from the beginning.
Is React Native itself too slow for a real app?
No. React Native renders native views, and its JavaScript engine, Hermes, is on by default. Plenty of large apps ship on it at a smooth frame rate. When an app is slow, the cause is almost always the code on top of the framework, which is good news, because code can be fixed.
How do I know whether the lag is JavaScript or native?
Turn on the Perf Monitor from the developer menu. It shows the JS frame rate and the UI frame rate separately. If the JS number drops while the UI number holds, your JavaScript is doing too much work; if the UI number drops, look at heavy views, shadows, oversized images, or layout.
Should I replace FlatList with FlashList?
For long or complex lists, it is often the single biggest win, because FlashList recycles cell components instead of mounting new ones, and its props are close to FlatList's. For a short list of a dozen rows, a plain map or a tuned FlatList is fine.
Can I ask the AI to fix the performance itself?
Yes, if you give it evidence. Paste the profiler output or name the slow screen and the measured frame drop, and ask for specific changes: memoize this row, move this animation to the native driver, switch this list to FlashList. A vague 'make it faster' tends to produce churn without a measurable gain.
Keep reading
Free React Native App Templates (2026): The Best Ones
The best free React Native app templates in 2026, from UI Kitten to Rootstrap and Obytes, on the modern Expo stack, plus why a template still needs a design.
How to Make a React Native App Look Good (2026)
How to make a React Native app look good: the NativeWind foundation, component libraries, native touches, and the design direction that avoids a generic look.
v0 by Vercel mobile app export: from web to a real iOS app
v0 by Vercel generates web React, so there is no direct mobile export. Here are the three real paths from a v0 design to an iOS app, and when each fits.
Can Cursor Build a Full React Native App From Scratch?
Yes, Cursor can scaffold and build a full React Native app, but it is a coding agent, not a hosted builder. Here is the realistic workflow and where it struggles.
How AI App Builders Actually Compile to Native Code
Are AI app builders really native? Most emit React Native or Expo, a few wrap a web view. Here is what each one ships and how App Store risk changes.
21st.dev-Style Live Preview for Mobile App Templates
Want a 21st.dev-style live preview for mobile? Browse VP0 designs that preview live, then run the generated code in Expo for instant fast-refresh.