¿Cómo se comparan realmente nativo, React Native y Flutter en rendimiento real?
Los benchmarks sintéticos miden lo que no importa — el rendimiento real depende del arranque, el jank al hacer scroll y la memoria bajo carga real.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Cómo se comparan realmente nativo, React Native y Flutter en rendimiento real?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#La pregunta detrás de la pregunta
Cuando un entrevistador hace esta pregunta, casi nunca busca un "nativo es más rápido, obvio". Quiere saber si entiendes por qué cada enfoque rinde como rinde —el modelo de renderizado, el modelo de hilos y dónde están realmente los cuellos de botella— porque ese entendimiento es lo que te permite defender una elección de framework para un producto concreto, y diagnosticar una pantalla lenta una vez que ya la lanzaste.
#Tres filosofías de renderizado distintas
El techo de rendimiento de cada enfoque lo define cómo convierte tu código en píxeles:
- Nativo (Swift/UIKit o SwiftUI en iOS, Kotlin/Jetpack Compose en Android) compila directamente al toolkit de widgets de la plataforma. No hay capa intermedia entre tu código de layout y el motor de renderizado del sistema operativo. Cada animación, cada evento de scroll, cada gesto lo maneja código que el fabricante de la plataforma afinó específicamente para ese hardware.
- React Native ejecuta la lógica de tu aplicación en un motor de JavaScript (Hermes, por defecto desde RN 0.70+) y renderiza usando los widgets nativos reales — un
<View>se convierte en unUIViewo unandroid.view.Viewde verdad. Históricamente, JS y nativo se comunicaban mediante un "bridge" asíncrono que serializaba cada llamada a JSON y la agrupaba en una cola de mensajes por lotes. La New Architecture (renderer Fabric + JSI + TurboModules) reemplazó eso con bindings en C++ directos y en su mayoría síncronos, eliminando el costo de serialización para la gran mayoría de las llamadas. - Flutter compila Dart a código máquina nativo ARM/x64 (AOT en builds de release, JIT en desarrollo para el hot reload) y luego dibuja todo él mismo —texto, sombras, botones— usando su propio motor de renderizado (Skia históricamente, Impeller en iOS y cada vez más en Android desde Flutter 3.x). Nunca toca
UIViewniandroid.view.View. Esto compra consistencia píxel a píxel entre plataformas al costo de reimplementar a mano la apariencia nativa de cada una.
#Dónde pierde tiempo cada uno realmente
Arranque (tiempo hasta el primer frame). Nativo gana casi por defecto — no hay runtime que arrancar. Flutter va muy cerca: el snapshot AOT de Dart carga rápido y el costo de inicialización del motor está en el orden de decenas de milisegundos. React Native paga el arranque del motor JS y el parseo del bundle; Hermes cerró la mayor parte de esta brecha al distribuir bytecode precompilado en lugar de código fuente, lo cual elimina el paso de parseo, pero un bundle JS grande y sin optimizar (o una cadena lenta de inicializaciones de módulos nativos disparándose al lanzar) todavía puede notarse claramente en una traza de arranque en frío.
Rendimiento de scroll y jank. Esto depende menos del framework que de si la lista está virtualizada. Un ScrollView ingenuo que mapea 5.000 elementos va a tener jank en cualquier framework, incluido un UITableView nativo mal usado. Con un esfuerzo equivalente, un UICollectionView/RecyclerView nativo y el ListView.builder de Flutter (que recicla objetos de vista y compone de forma independiente a la lógica de la aplicación) tienden a mantener 60/120fps de forma más confiable. El FlatList de React Native, combinado con la capacidad de la New Architecture de confirmar el layout del lado nativo sin esperar a un hilo JS ocupado, cerró la mayor parte —no toda— de la brecha histórica.
Modelo de hilos. Nativo te da control total: colas de GCD, corrutinas de Kotlin, lo que quieras. Flutter ejecuta tu código Dart en un único isolate de UI por defecto (los isolates no comparten memoria, así que el paralelismo significa levantar más isolates y pasar mensajes, no hilos con memoria compartida) —pero la composición ocurre en un hilo de raster separado, así que un isolate de UI ocupado degrada la interacción pero no necesariamente descarta frames ya programados. React Native mantiene JS en su propio hilo; el trabajo JS síncrono pesado (un JSON.parse grande, una cascada de re-renders sin memoizar) bloquea ese hilo, por lo cual delegar trabajo costoso (procesamiento de imágenes, criptografía, cómputo pesado) a un módulo nativo o a un runtime JS en segundo plano es una mitigación estándar.
Memoria y tamaño del binario. Flutter empaqueta el runtime de Dart y el motor Skia/Impeller dentro de la app, típicamente sumando varios megabytes al tamaño del binario y algo de sobrecarga de memoria base. React Native empaqueta el motor Hermes (más pequeño que JSC o V8) más la infraestructura de bridge/JSI. Nativo no tiene ninguno de estos costos fijos. En un dispositivo Android gama baja con memoria limitada, esta diferencia base suele ser más perceptible para el usuario que cualquiera de las anteriores.
#La comparación, lado a lado
| Dimensión | Nativo (Swift/Kotlin) | React Native (New Architecture) | Flutter |
|---|---|---|---|
| Renderizado | Widgets nativos reales, sin capa intermedia | Widgets nativos reales vía Fabric + JSI | Dibuja todo con Skia/Impeller, sin widgets de la plataforma |
| Arranque en frío | El más rápido | Mejoró mucho con el bytecode de Hermes; aún tiene costo de init de JS | Casi nativo, poco overhead de init de AOT |
| Scroll/jank a escala | Mejor con virtualización correcta de listas | Bueno con FlatList y memoización adecuada; históricamente el punto débil | Muy bueno, hilo de raster dedicado |
| Hilos | Control nativo total (GCD, corrutinas) | Un hilo JS + hilos nativos; JSI elimina la mayor parte del costo de serialización | Isolates de Dart; paso de mensajes, no memoria compartida |
| Overhead de tamaño de binario | Ninguno | + motor Hermes + librerías de JSI/bridge | + runtime de Dart + Skia/Impeller |
| Memoria base | La más baja | Moderada | Más alta, según el motor |
| Apariencia de plataforma | Automática, siempre actualizada | Automática (los widgets reales la heredan) | Manual — los widgets Material/Cupertino la aproximan |
| Velocidad de desarrollo | La más lenta (dos bases de código nativas) | Rápida — hot reload, una sola base de código | Rápida — hot reload, una sola base de código |
#La verdad incómoda: la arquitectura le gana a la elección de framework
En la práctica, la palanca de rendimiento más grande en cualquiera de estos stacks no es el framework — es si virtualizas tus listas, evitas re-renders/rebuilds innecesarios, delegas trabajo pesado fuera del hilo principal/de UI, y usas el driver de animación nativo (useNativeDriver/worklets de Reanimated en RN, AnimatedBuilder acelerado por hardware en Flutter, Core Animation en nativo). Una app nativa mal construida va a perder contra una app Flutter o React Native bien construida en cada uno de estos ejes. Cuando un entrevistador insiste en "cuál es más rápido", la respuesta más sólida nombra los tradeoffs a nivel de mecanismo de arriba y dice que, en la práctica, la disciplina de ingeniería dentro del framework elegido suele dominar por encima del techo propio del framework.
🧠 Comprueba tu comprensión
0/2 · 0/2 answered1. ¿Qué cambió realmente entre el bridge antiguo de React Native y la New Architecture (JSI/Fabric) respecto al rendimiento?
2. ¿Por qué Flutter suele tener un binario más grande y mayor memoria base que una app nativa equivalente?