Introducción: El Rendimiento Ya No Es Opcional
Seamos honestos: si tu app no es fluida, los usuarios se van. Así de simple. En 2026, la gente espera transiciones a 60 FPS (y 120 FPS en dispositivos de gama alta), tiempos de carga casi instantáneos y una experiencia que se sienta genuinamente nativa. La buena noticia es que React Native ya puede ofrecer todo eso.
Pero solo si sabes cómo aprovecharlo.
Con la Nueva Arquitectura completamente establecida, React Native 0.79 en circulación, el React Compiler 1.0 estable y Reanimated 4 disponible, el ecosistema ha madurado de manera impresionante. Aun así, tener herramientas potentes no garantiza resultados — la diferencia entre una app que vuela y una que se arrastra está en los detalles de implementación.
En esta guía vamos a recorrer las técnicas de optimización más efectivas para React Native en 2026. Desde la configuración de la Nueva Arquitectura hasta la optimización de listas, animaciones, arranque y perfilado. Todo con código práctico que puedes aplicar hoy mismo. Así que, vamos al grano.
La Nueva Arquitectura: El Cimiento del Rendimiento Moderno
JSI, Fabric y TurboModules — Lo Que Realmente Cambió
Si todavía no has migrado a la Nueva Arquitectura, este es el momento. Con React Native 0.82, la arquitectura antigua fue deshabilitada permanentemente. Pero más allá de que ya no tienes opción, entender los componentes internos te ayuda a escribir código más eficiente.
La arquitectura anterior usaba un puente (bridge) asíncrono que serializaba datos en JSON para comunicar JavaScript con el código nativo. Era lento y un cuello de botella importante. La nueva arquitectura lo reemplaza con tres pilares:
- JSI (JavaScript Interface): Permite que JavaScript llame directamente a funciones nativas de forma síncrona, sin serialización JSON. Es como pasar de enviar cartas a hablar por teléfono — la diferencia se nota al instante.
- Fabric: El nuevo motor de renderizado que soporta renderizado concurrente e interrumpible, alineado con React 18+. Las actualizaciones de UI son más fluidas porque el hilo principal ya no se bloquea con tanta frecuencia.
- TurboModules: Los módulos nativos ahora se cargan bajo demanda (lazy loading) en lugar de todos al inicio. Esto reduce drásticamente el tiempo de arranque, y créeme, tus usuarios lo notan.
Verificar y Habilitar la Nueva Arquitectura
En proyectos creados con Expo SDK 52 o superior y React Native 0.76+, la Nueva Arquitectura viene habilitada por defecto. Para verificarlo:
// app.json (Expo)
{
"expo": {
"newArchEnabled": true,
"plugins": [
"expo-router"
]
}
}
Para proyectos bare (sin Expo), la configuración varía según la plataforma:
// android/gradle.properties
newArchEnabled=true
// iOS - en el Podfile
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
Una vez habilitada, asegúrate de que todas tus dependencias de terceros sean compatibles. La gran mayoría de las bibliotecas populares ya lo soportan, pero siempre conviene verificar antes de migrar un proyecto en producción. Te ahorrarás más de un dolor de cabeza.
React Compiler: Memoización Automática Sin Esfuerzo
¿Qué Hace Exactamente el React Compiler?
Sinceramente, el React Compiler 1.0 es probablemente el avance más impactante para el rendimiento en React Native este año. Es una herramienta en tiempo de compilación que analiza automáticamente el flujo de datos de tus componentes y aplica memoización donde sea necesario — sin que escribas una sola línea de useMemo, useCallback o React.memo.
¿Los resultados? Hablan por sí solos. Meta reporta mejoras del 12% en tiempos de carga y hasta 2.5x más rápido en interacciones. Sanity Studio logró una reducción del 20-30% en tiempos de renderizado tras compilar el 87% de sus componentes. No está mal para algo que básicamente funciona solo.
Configuración en Expo
A partir de Expo SDK 54, el React Compiler viene habilitado por defecto. Para versiones anteriores, puedes activarlo manualmente:
// babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
['babel-plugin-react-compiler', {
// Opciones del compilador
target: '19' // Versión de React objetivo
}]
],
};
};
Escribir Código Compatible con el Compilador
El compilador funciona mejor cuando sigues las reglas de React. Aquí tienes un ejemplo claro de lo que sí y lo que no debes hacer:
// ✅ BUENO: Componente que el compilador puede optimizar fácilmente
function ProductCard({ product, onPress }) {
const formattedPrice = `$${product.price.toFixed(2)}`;
return (
<Pressable onPress={() => onPress(product.id)}>
<View style={styles.card}>
<Text style={styles.name}>{product.name}</Text>
<Text style={styles.price}>{formattedPrice}</Text>
</View>
</Pressable>
);
}
// ❌ MALO: Mutación directa que confunde al compilador
function ProductCard({ product, onPress }) {
product.price = product.price * 1.21; // ¡Mutación directa de props!
return (
<Pressable onPress={() => onPress(product.id)}>
<View style={styles.card}>
<Text>{product.name}</Text>
<Text>{product.price}</Text>
</View>
</Pressable>
);
}
Las reglas clave: no mutar props ni estado directamente, mantener los hooks en el nivel superior del componente, y seguir las reglas estándar de React. Un dato interesante: el compilador puede memoizar valores condicionales, algo que no es posible hacer manualmente con useMemo.
¿Debo Eliminar useMemo y useCallback Existentes?
No inmediatamente. El compilador detecta y respeta la memoización manual existente, así que no hay conflicto. Con el tiempo, puedes ir eliminando esas llamadas para simplificar tu código y dejar que el compilador haga lo suyo. Pero no hay prisa.
Optimización de Listas: FlatList y FlashList
Configuración Óptima de FlatList
Las listas son, sin duda, uno de los puntos más críticos de rendimiento en cualquier app móvil. He visto apps que funcionaban perfectamente hasta que mostraban una lista de 200 elementos sin configurar correctamente. Un FlatList mal configurado puede causar caídas de frames, consumo excesivo de memoria y una experiencia terrible.
Aquí está la configuración que recomiendo como punto de partida:
import { FlatList, StyleSheet, View, Text } from 'react-native';
import { memo, useCallback } from 'react';
// Componente de item memoizado
const ProductItem = memo(function ProductItem({ item, onPress }) {
return (
<Pressable onPress={() => onPress(item.id)}>
<View style={styles.item}>
<Text style={styles.title}>{item.name}</Text>
<Text style={styles.subtitle}>${item.price}</Text>
</View>
</Pressable>
);
});
function ProductList({ products }) {
const handlePress = useCallback((id) => {
// manejar la navegación
}, []);
const renderItem = useCallback(({ item }) => (
<ProductItem item={item} onPress={handlePress} />
), [handlePress]);
const keyExtractor = useCallback((item) => item.id.toString(), []);
// getItemLayout elimina la necesidad de medir cada elemento
const getItemLayout = useCallback((data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
}), []);
return (
<FlatList
data={products}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews={true}
updateCellsBatchingPeriod={50}
/>
);
}
const ITEM_HEIGHT = 80;
const styles = StyleSheet.create({
item: {
height: ITEM_HEIGHT,
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#e0e0e0',
},
title: {
fontSize: 16,
fontWeight: '600',
},
subtitle: {
fontSize: 14,
color: '#666',
marginTop: 4,
},
});
Parámetros Clave de FlatList Explicados
Cada parámetro de rendimiento tiene un propósito específico. Entender cuándo y cómo ajustarlos hace toda la diferencia:
- initialNumToRender: Número de elementos renderizados en el montaje inicial. Un valor de 10 suele ser suficiente para llenar la pantalla sin desperdiciar recursos.
- maxToRenderPerBatch: Cuántos elementos se renderizan por lote durante el scroll. Valores más altos reducen los espacios en blanco pero pueden causar jank. Es un equilibrio delicado.
- windowSize: Define el área de renderizado como múltiplo de la altura visible. Con un valor de 5, se renderizan 2 pantallas arriba, la visible, y 2 abajo.
- removeClippedSubviews: Desmonta las vistas fuera de pantalla. Especialmente útil para listas largas con elementos complejos.
- getItemLayout: Proporciona la altura de los elementos por adelantado, evitando medirlos dinámicamente. Es la optimización más impactante cuando los elementos tienen altura fija.
FlashList: Cuando FlatList No Es Suficiente
Si FlatList se queda corto para tu caso de uso (y en listas grandes, es probable), FlashList de Shopify es una alternativa que recicla celdas en lugar de destruirlas y recrearlas. Esto marca una gran diferencia con cientos o miles de elementos:
import { FlashList } from '@shopify/flash-list';
function ProductList({ products }) {
const renderItem = useCallback(({ item }) => (
<ProductItem item={item} />
), []);
return (
<FlashList
data={products}
renderItem={renderItem}
estimatedItemSize={80}
keyExtractor={(item) => item.id.toString()}
/>
);
}
FlashList recicla las vistas de manera similar a RecyclerView en Android y UICollectionView en iOS. La diferencia en la práctica puede ser enorme: en listas de más de 1000 elementos, FlashList mantiene 60 FPS consistentemente donde FlatList puede caer a 30 FPS o menos. Lo he comprobado en más de un proyecto.
Optimización del Arranque de la Aplicación
Hermes: Tu Motor de JavaScript Optimizado
Hermes es el motor de JavaScript de React Native, y en 2026 trae mejoras significativas. Su beneficio principal es la precompilación a bytecode: en lugar de interpretar JavaScript en tiempo de ejecución, Hermes compila el código durante la fase de build. El resultado es una reducción drástica del Time-to-Interactive (TTI).
En iOS, los benchmarks muestran que Hermes arranca la app un 40% más rápido que JavaScriptCore. Además, su recolector de basura generacional consume menos memoria y produce menos pausas. Eso se traduce en menos "tirones" durante la ejecución.
Hermes viene habilitado por defecto en React Native 0.76+. No necesitas configuración adicional, pero puedes verificar que está activo:
// Verificar si Hermes está activo
const isHermes = () => !!global.HermesInternal;
console.log('Hermes habilitado:', isHermes());
Bundles Sin Comprimir en Android (React Native 0.79)
React Native 0.79 introdujo una optimización curiosa para Android: los bundles de JavaScript ahora se incluyen sin comprimir dentro del APK. Suena contraproducente, ¿verdad? Pero la lógica es brillante: el sistema operativo puede mapear el archivo directamente en memoria sin descomprimirlo primero. El resultado es una mejora de hasta 400ms en el TTI (12% más rápido) en dispositivos como el Samsung Galaxy A14.
Esta optimización está habilitada por defecto en React Native 0.79+, así que solo necesitas actualizar para aprovecharla.
Metro: Arranque 3x Más Rápido
Metro, el bundler de React Native, también recibió mejoras notables en la versión 0.79. El uso de hashing diferido (deferred hashing) reduce los tiempos de arranque del bundler en más de tres veces, especialmente en proyectos grandes y monorepos. En la práctica, esto se traduce en ciclos de desarrollo más ágiles y tiempos de recarga más cortos.
Reducir el Tiempo de Arranque con Carga Diferida
Más allá de las optimizaciones del framework, hay técnicas que puedes aplicar directamente en tu código. La carga diferida es probablemente la más fácil de implementar y la que más impacto tiene:
import { lazy, Suspense } from 'react';
import { View, ActivityIndicator } from 'react-native';
// Carga diferida de pantallas pesadas
const AnalyticsDashboard = lazy(() => import('./screens/AnalyticsDashboard'));
const Settings = lazy(() => import('./screens/Settings'));
function LoadingFallback() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#007AFF" />
</View>
);
}
// En tu navegación
function AppNavigator() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Analytics">
{() => (
<Suspense fallback={<LoadingFallback />}>
<AnalyticsDashboard />
</Suspense>
)}
</Stack.Screen>
<Stack.Screen name="Settings">
{() => (
<Suspense fallback={<LoadingFallback />}>
<Settings />
</Suspense>
)}
</Stack.Screen>
</Stack.Navigator>
);
}
Otra técnica que me ha funcionado muy bien es diferir las inicializaciones que no son críticas para la primera pantalla:
import { InteractionManager } from 'react-native';
import { useEffect } from 'react';
function App() {
useEffect(() => {
// Ejecutar después de que la primera pantalla se haya renderizado
InteractionManager.runAfterInteractions(() => {
// Inicializar analytics
initAnalytics();
// Precargar datos secundarios
prefetchUserPreferences();
// Configurar notificaciones push
setupPushNotifications();
});
}, []);
return <AppNavigator />;
}
Animaciones de Alto Rendimiento con Reanimated 4
CSS Animations: Simplicidad y Rendimiento
Reanimated 4 trae un cambio de paradigma bastante interesante: ahora puedes definir animaciones usando una sintaxis compatible con CSS. Estas animaciones declarativas son más fáciles de optimizar porque la biblioteca entiende exactamente qué propiedades se animan. Y lo mejor: se ejecutan directamente en el hilo de la UI, garantizando 60-120 FPS.
import Animated, {
useAnimatedStyle,
withTiming,
withSpring,
useSharedValue,
} from 'react-native-reanimated';
function AnimatedCard() {
const scale = useSharedValue(1);
const opacity = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: withSpring(scale.value) }],
opacity: withTiming(opacity.value, { duration: 200 }),
}));
const handlePressIn = () => {
scale.value = 0.95;
opacity.value = 0.8;
};
const handlePressOut = () => {
scale.value = 1;
opacity.value = 1;
};
return (
<Pressable onPressIn={handlePressIn} onPressOut={handlePressOut}>
<Animated.View style={[styles.card, animatedStyle]}>
<Text style={styles.cardText}>Toca para animar</Text>
</Animated.View>
</Pressable>
);
}
Worklets para Animaciones Complejas
Para escenarios más avanzados — animaciones vinculadas a gestos, transiciones de pantalla o animaciones derivadas — los worklets siguen siendo tu mejor aliado. Se ejecutan en el hilo de la UI sin pasar por el puente de JavaScript, lo que garantiza la máxima fluidez:
import Animated, {
useAnimatedScrollHandler,
useAnimatedStyle,
useSharedValue,
interpolate,
Extrapolation,
} from 'react-native-reanimated';
const HEADER_HEIGHT = 200;
function ParallaxHeader() {
const scrollY = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
scrollY.value = event.contentOffset.y;
},
});
const headerStyle = useAnimatedStyle(() => ({
height: interpolate(
scrollY.value,
[0, HEADER_HEIGHT],
[HEADER_HEIGHT, 80],
Extrapolation.CLAMP
),
opacity: interpolate(
scrollY.value,
[0, HEADER_HEIGHT / 2],
[1, 0.3],
Extrapolation.CLAMP
),
}));
const imageStyle = useAnimatedStyle(() => ({
transform: [
{
scale: interpolate(
scrollY.value,
[-100, 0],
[1.5, 1],
Extrapolation.CLAMP
),
},
{
translateY: interpolate(
scrollY.value,
[0, HEADER_HEIGHT],
[0, -HEADER_HEIGHT / 3],
Extrapolation.CLAMP
),
},
],
}));
return (
<View style={{ flex: 1 }}>
<Animated.View style={[styles.header, headerStyle]}>
<Animated.Image
source={require('./header-bg.jpg')}
style={[styles.headerImage, imageStyle]}
/>
</Animated.View>
<Animated.ScrollView
onScroll={scrollHandler}
scrollEventThrottle={16}
>
<View style={{ paddingTop: HEADER_HEIGHT }}>
{/* Contenido de la lista */}
</View>
</Animated.ScrollView>
</View>
);
}
Errores Comunes en Animaciones
Estos son los errores que veo con más frecuencia (y que más impactan el rendimiento):
- Usar el API Animated de React Native en lugar de Reanimated: El Animated estándar ejecuta las animaciones en el hilo de JavaScript, causando jank cuando hay carga computacional. Es la fuente número uno de animaciones entrecortadas.
- Animar propiedades de layout: Animar
width,height,marginopaddingcausa re-layouts costosos. Prefieretransformyopacity, que se procesan directamente en la GPU. - No usar scrollEventThrottle={16}: Sin este valor, los eventos de scroll se reciben con menos frecuencia y las animaciones no son fluidas. Parece un detalle menor, pero marca una diferencia enorme.
- Crear shared values dentro de useEffect: Los shared values deben crearse en el nivel superior del componente, no dentro de efectos. Es un error sutil que puede costarte horas de depuración.
Optimización de Imágenes y Recursos
Gestión Eficiente de Imágenes
Las imágenes suelen ser los recursos más pesados en una app móvil. Una gestión incorrecta puede causar consumo excesivo de memoria y tiempos de carga frustrantes. La buena noticia es que con las herramientas actuales, optimizarlas es bastante directo:
import { Image } from 'expo-image';
function OptimizedImageGallery({ images }) {
return (
<FlatList
data={images}
renderItem={({ item }) => (
<Image
source={{ uri: item.thumbnailUrl }}
style={styles.thumbnail}
contentFit="cover"
transition={200}
placeholder={{ blurhash: item.blurhash }}
recyclingKey={item.id}
cachePolicy="memory-disk"
/>
)}
keyExtractor={(item) => item.id}
initialNumToRender={6}
/>
);
}
La biblioteca expo-image tiene varias ventajas sobre el componente Image estándar de React Native:
- Caché en memoria y disco: Las imágenes se almacenan automáticamente para acceso rápido. No más descargas repetidas.
- Blurhash placeholder: Muestra un preview borroso mientras se carga la imagen completa. Mejora mucho la percepción de velocidad.
- Reciclaje de componentes: Con
recyclingKey, el componente reutiliza las vistas de imagen en listas virtualizadas. - Transiciones suaves: Las imágenes aparecen con una transición animada en lugar de un salto abrupto. Un detalle pequeño que mejora la experiencia.
Tamaño Correcto de Imágenes
Nunca cargues imágenes más grandes de lo necesario. Si tu thumbnail tiene 80x80 puntos y el dispositivo tiene un pixel ratio de 3, la imagen óptima sería de 240x240 píxeles. Muchos CDN soportan redimensionamiento directamente en la URL:
import { PixelRatio } from 'react-native';
function getOptimizedImageUrl(baseUrl, width, height) {
const pixelRatio = PixelRatio.get();
const optimalWidth = Math.round(width * pixelRatio);
const optimalHeight = Math.round(height * pixelRatio);
return `${baseUrl}?w=${optimalWidth}&h=${optimalHeight}&fit=cover&q=80`;
}
Perfilado y Depuración de Rendimiento
React DevTools Profiler
El Profiler de React DevTools te permite visualizar cuántas veces se renderizan tus componentes y cuánto toma cada render. Es la primera herramienta a la que deberías acudir cuando sospechas de problemas de rendimiento:
// Envuelve tu app con el Profiler para capturar métricas
import { Profiler } from 'react';
function onRenderCallback(
id, // ID del árbol de Profiler
phase, // "mount" o "update"
actualDuration, // Tiempo de renderizado en ms
baseDuration, // Tiempo estimado sin memoización
startTime,
commitTime,
) {
if (actualDuration > 16) { // Más de un frame a 60fps
console.warn(
`Render lento en "${id}": ${actualDuration.toFixed(2)}ms (${phase})`
);
}
}
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<AppNavigator />
</Profiler>
);
}
Perfilado con Hermes
Hermes incluye un profiler integrado que muestra exactamente qué funciones consumen más CPU. Puedes generar un perfil desde tu código y analizarlo en Chrome DevTools:
// Iniciar el profiler de Hermes
if (global.HermesInternal) {
global.HermesInternal.enableSampling();
// Después de un período de tiempo, detener y exportar
setTimeout(() => {
global.HermesInternal.disableSampling();
// El perfil se puede exportar con hermes-profile-transformer
}, 10000);
}
También puedes usar react-native-release-profiler de Margelo para perfilar en builds de producción de forma pasiva, sin impacto significativo en el rendimiento. Es particularmente útil para detectar problemas que solo aparecen con datos reales.
Flipper y las Herramientas Nativas
Para problemas de rendimiento a nivel nativo, vas a necesitar herramientas específicas de cada plataforma:
- Xcode Instruments: Ideal para perfilar uso de CPU, memoria y GPU en iOS. El instrumento "Time Profiler" te muestra exactamente dónde se gasta el tiempo del hilo principal.
- Android Studio Profiler: Ofrece perfilado de CPU, memoria, red y energía. El "CPU Profiler" con method recording te ayuda a identificar cuellos de botella nativos.
- Perf Monitor de React Native: Activa el monitor de rendimiento en el menú de desarrollo (sacude el dispositivo o Cmd+D en el simulador) para ver los FPS del hilo de JavaScript y del hilo de la UI en tiempo real.
Gestión de Estado Eficiente
Evitar Re-renderizados Innecesarios
La gestión de estado es una fuente frecuente de problemas de rendimiento, y a veces de las más difíciles de diagnosticar. Cada cambio de estado causa un re-renderizado del componente y de todos sus hijos. La clave está en minimizar el alcance:
// ❌ MALO: Estado global que causa re-renders masivos
function App() {
const [state, setState] = useState({
user: null,
products: [],
cart: [],
searchQuery: '',
filters: {},
});
// Cada vez que cambia searchQuery, TODA la app se re-renderiza
const handleSearch = (query) => {
setState(prev => ({ ...prev, searchQuery: query }));
};
}
// ✅ BUENO: Estado localizado cerca de donde se usa
function SearchBar() {
const [searchQuery, setSearchQuery] = useState('');
// Solo SearchBar se re-renderiza con cada keystroke
return (
<TextInput
value={searchQuery}
onChangeText={setSearchQuery}
placeholder="Buscar productos..."
/>
);
}
Zustand para Estado Global Eficiente
Cuando necesitas estado global (y en algún momento lo vas a necesitar), Zustand es una opción excelente. Permite seleccionar exactamente qué parte del estado necesita cada componente, minimizando los re-renderizados de forma natural:
import { create } from 'zustand';
const useStore = create((set) => ({
user: null,
cart: [],
addToCart: (product) =>
set((state) => ({
cart: [...state.cart, product],
})),
clearCart: () => set({ cart: [] }),
}));
// Solo se re-renderiza cuando cambia cart.length
function CartBadge() {
const itemCount = useStore((state) => state.cart.length);
return <Text>{itemCount}</Text>;
}
// Solo se re-renderiza cuando cambia user
function UserAvatar() {
const user = useStore((state) => state.user);
return <Avatar source={{ uri: user?.avatarUrl }} />;
}
Optimizaciones de Red
React Query para Datos del Servidor
La forma en que manejas las llamadas a la API impacta directamente en el rendimiento percibido. React Query (TanStack Query) se ha convertido en el estándar para gestionar datos del servidor, y por buenas razones: caché inteligente, revalidación en segundo plano y deduplicación de peticiones, todo incluido:
import { useQuery, useQueryClient } from '@tanstack/react-query';
function ProductDetail({ productId }) {
const { data, isLoading, error } = useQuery({
queryKey: ['product', productId],
queryFn: () => fetchProduct(productId),
staleTime: 5 * 60 * 1000, // 5 minutos antes de considerarlo obsoleto
gcTime: 30 * 60 * 1000, // 30 minutos en caché
});
if (isLoading) return <ProductSkeleton />;
if (error) return <ErrorView error={error} />;
return <ProductView product={data} />;
}
// Prefetching: cargar datos antes de que el usuario los necesite
function ProductList({ products }) {
const queryClient = useQueryClient();
const handleProductHover = (productId) => {
queryClient.prefetchQuery({
queryKey: ['product', productId],
queryFn: () => fetchProduct(productId),
});
};
return (
<FlatList
data={products}
renderItem={({ item }) => (
<Pressable
onPressIn={() => handleProductHover(item.id)}
onPress={() => navigateToProduct(item.id)}
>
<ProductCard product={item} />
</Pressable>
)}
/>
);
}
Lista de Verificación de Rendimiento
Antes de lanzar tu app a producción, pasa por esta checklist. Es fácil olvidar algún punto cuando estás en medio de un sprint:
- Nueva Arquitectura habilitada — Fabric y TurboModules activos.
- Hermes como motor JS — Verificar que está activo y funcionando correctamente.
- React Compiler configurado — Memoización automática en acción.
- Listas optimizadas — FlatList bien configurado o FlashList para listas grandes.
- Imágenes optimizadas — Tamaños correctos, caché habilitado, placeholders configurados.
- Animaciones en el hilo de UI — Reanimated para todas las animaciones complejas.
- Estado localizado — Estado lo más cerca posible de donde se usa.
- Carga diferida — Pantallas secundarias cargadas con lazy() y Suspense.
- Perfilado en dispositivo real — Siempre probar en dispositivos físicos, no solo en el simulador.
- Bundle analizado — Sin dependencias innecesarias inflando el tamaño del bundle.
Conclusión: El Rendimiento Es un Proceso, No un Destino
Optimizar una aplicación React Native no es algo que haces una vez y te olvidas. Es un proceso continuo que empieza con buenas decisiones arquitectónicas — la Nueva Arquitectura, Hermes, el React Compiler — y se refina con técnicas específicas para cada situación.
Lo positivo es que el ecosistema de React Native en 2026 está en un punto donde el rendimiento por defecto es significativamente mejor que hace un par de años. Con la arquitectura antigua eliminada, el React Compiler generando optimizaciones automáticas y Hermes madurando como motor de JavaScript, la base sobre la que construyes ya es sólida.
Mi consejo: empieza por las optimizaciones de mayor impacto con menor esfuerzo — habilitar el React Compiler, configurar correctamente FlatList, y usar Reanimated para animaciones. Después, usa las herramientas de perfilado para encontrar los cuellos de botella específicos de tu app.
Y por favor, siempre prueba en dispositivos reales. El simulador miente. Los usuarios no aceptan nada menos que fluidez en 2026, y con lo que hemos cubierto en esta guía, tienes todo lo necesario para dársela.