|

Cómo Optimizar React – Guía 2026

Fuentes

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Prioridad de optimización por impacto esperado:

  • Code splitting: 40-60% reducción en JS inicial (máximo impacto en LCP)
  • Virtualización: O(n) → O(1) en listas grandes (máximo impacto en fluidez)
  • Context splitting: 30-70% reducción en cascadas (impacto en tiempos de interacción)
  • React.memo + useCallback: 20-50% reducción en renders (impacto moderado, depende del caso)

Preguntas frecuentes

¿React.memo es igual a PureComponent?

Funciona similar pero React.memo es para funcionales, PureComponent para clases. React.memo compara props superficialmente por defecto (Object.is). Si necesitás comparación personalizada, pasá tu propio areEqual.

¿Cuál es el límite de tamaño de bundle antes de que sea un problema?

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Paso 2: Entendé por qué se re-renderizó. ¿Cambió la prop? ¿Cambió el estado del padre? ¿Es un re-render en cascada desde Context? La causa sugiere la solución.

Paso 3: Aplicá la técnica específica. Si el problema es props que cambian constantemente, React.memo + useCallback. Si es estado global, dividí Contexts. Si es bundle grande, code split. Si es lista larga, virtualiza.

Paso 4: Verificá el impacto. Grabá de nuevo con Profiler. ¿Bajó el tiempo? ¿20ms → 8ms? Bueno. ¿Sigue en 20ms? La técnica no era la adecuada, intent otra.

Prioridad de optimización por impacto esperado:

  • Code splitting: 40-60% reducción en JS inicial (máximo impacto en LCP)
  • Virtualización: O(n) → O(1) en listas grandes (máximo impacto en fluidez)
  • Context splitting: 30-70% reducción en cascadas (impacto en tiempos de interacción)
  • React.memo + useCallback: 20-50% reducción en renders (impacto moderado, depende del caso)

Preguntas frecuentes

¿React.memo es igual a PureComponent?

Funciona similar pero React.memo es para funcionales, PureComponent para clases. React.memo compara props superficialmente por defecto (Object.is). Si necesitás comparación personalizada, pasá tu propio areEqual.

¿Cuál es el límite de tamaño de bundle antes de que sea un problema?

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Soluciones:

  • Dividí Contexts por dominio: AuthContext para auth, UIContext para temas/modales, DataContext para datos. Así un cambio en tema no dispara re-render de componentes de datos.
  • Usá custom hooks para selectores: en vez de consumir todo el Context, creá hooks que extraen solo lo que necesitás.
  • Considerá librerías especializadas: Zustand, Redux, Recoil manejan updates granulares mejor. Si tu app es simple, Context alcanza. Si es compleja, especializa.

El pattern común de error: meter TODO en un Context global, App state, user state, UI state, feature flags. Un cambio en feature flags hace re-render de toda la app. Mala idea.

React Compiler y Server Components: 2026

React 19.2+ trae React Compiler, una herramienta que automáticamente memoiza dependencias. Vos escribís código simple, Compiler inserta useMemo y useCallback donde necesitás. Es como tener un botón que optimiza todo automáticamente. Todavía está en desarrollo pero es el futuro: menos trabajo manual, más optimizaciones.

Server Components en Next.js van más lejos: el componente renderiza en el servidor, no en el cliente. Solo la salida HTML llega al navegador. Beneficio: reducís JS del cliente dramáticamente. Una página que pesa 200KB de JS del cliente puede pesar 40KB con Server Components. El tradeoff: menos interactividad real-time en ciertos casos, pero para la mayoría de casos es un win. En herramientas con IA para mejorar desarrollo profundizamos sobre esto.

Tabla comparativa: técnicas de optimización y cuándo usarlas

TécnicaCuándo usarlaImpacto esperadoComplejidad
React.memoComponentes costosos con props establesReduce re-renders innecesarios 30-50%Baja
useMemo / useCallbackValores/funciones costosas, dependencias clarasReduce cálculos repetidos 20-40%Media
Code splittingApps medianas/grandes con múltiples rutasReduce JS inicial 40-60%Baja (automático en Next.js)
VirtualizaciónListas/tablas con 100+ itemsRenderizado O(1), fluido siempreMedia
Context splittingApps con state global frecuenteReduce cascadas de re-render 30-70%Media
Server ComponentsContenido estático o generado en servidorReduce JS cliente 60-80%Alta (requiere Next.js)
optimizar react diagrama explicativo

Errores comunes que cometés (sin saberlo)

Error 1: Memoizar todo por si acaso. Desarrolladores juniors meten React.memo en todos lados pensando que es magia. La verdad es que React.memo cuesta CPU. Si no hay re-render innecesario, no hay problema que solucionar. Medí primero.

Error 2: Asumir que un estado global es un problema sin verificar. “Mi app es lenta, seguro es porque uso Context global.” A veces la culpa es una query pesada, un loop en renderizado, o un plugin de navegador. Profilea antes de refactorizar toda tu arquitectura de estado.

Error 3: No usar code splitting en rutas.** Tenés 50 páginas en un SPA y un solo bundle de 2MB. El usuario que solo necesita ver 3 páginas descarga 47 páginas que nunca va a visitar. Code splitting es casi gratis en Next.js y webpack.

Error 4: Crear derivados costosos en render.** Calcular filtros, ordenamientos, maps gigantes dentro del render cada vez que pasa. Memoizá eso o movelo fuera.

Checklist práctico: cómo aplicar optimización ahora

Paso 1: Medir. Abrí React DevTools Profiler, grabá una interacción de usuario, identificá el componente que tardó más de 20ms. Ese es tu candidato. Cubrimos ese tema en detalle en repositorios y control de versiones modernos.

Paso 2: Entendé por qué se re-renderizó. ¿Cambió la prop? ¿Cambió el estado del padre? ¿Es un re-render en cascada desde Context? La causa sugiere la solución.

Paso 3: Aplicá la técnica específica. Si el problema es props que cambian constantemente, React.memo + useCallback. Si es estado global, dividí Contexts. Si es bundle grande, code split. Si es lista larga, virtualiza.

Paso 4: Verificá el impacto. Grabá de nuevo con Profiler. ¿Bajó el tiempo? ¿20ms → 8ms? Bueno. ¿Sigue en 20ms? La técnica no era la adecuada, intent otra.

Prioridad de optimización por impacto esperado:

  • Code splitting: 40-60% reducción en JS inicial (máximo impacto en LCP)
  • Virtualización: O(n) → O(1) en listas grandes (máximo impacto en fluidez)
  • Context splitting: 30-70% reducción en cascadas (impacto en tiempos de interacción)
  • React.memo + useCallback: 20-50% reducción en renders (impacto moderado, depende del caso)

Preguntas frecuentes

¿React.memo es igual a PureComponent?

Funciona similar pero React.memo es para funcionales, PureComponent para clases. React.memo compara props superficialmente por defecto (Object.is). Si necesitás comparación personalizada, pasá tu propio areEqual.

¿Cuál es el límite de tamaño de bundle antes de que sea un problema?

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

React.lazy() y Suspense lo hacen simple:

const AdminPanel = React.lazy(() => import('./AdminPanel'));

Cuando el usuario navega a /admin, React carga dinamicamente el chunk de AdminPanel. Mientras se descarga, mostrás un Suspense fallback (spinner, esqueleto). Según benchmarks 2025, code splitting puede reducir el JS inicial entre 40-60%. Si tu app actual pesa 800KB y dividís en 5 chunks, el usuario baja 160KB inicialmente. El impacto en LCP es directo.

Herramientas como webpack (que Next.js usa) hacen esto automáticamente si usás dynamic imports. Si estás en Next.js, ya pasa: cada página es un chunk separado.

Virtualización de listas: renderizar solo lo visible

Tenés una tabla de 10.000 items. Cada item es un componente con cálculos, eventos, estado local. Si renderizás todos los 10.000, la pestaña se congela. El viewport solo muestra 20. ¿Por qué renderizar los otros 9.980? Complementá con consideraciones de seguridad en tu código.

Virtualización renderiza solo los items dentro del viewport más un buffer. Cuando scrolleás, renderiza los nuevos y destruye los que salieron. Es un cambio de paradigma de O(n) renders a O(1). Librerías como react-window (recomendada en 2025) manejan el cálculo automáticamente. Vos le pasás el largo total, la altura del item, y una función que renderiza cada item por índice. Virtualización cuida el resto.

Caso real: un feed infinito de redes sociales con 5.000+ posts. Sin virtualización, después de scrollear un rato la app explota. Con virtualización, podés scrollear infinitamente sin lag. El cambio de usuario final es dramático.

Gestión de estado: Context API y sus límites

Context API es fácil de usar pero tiene una trampa: cuando cambia un valor en el Context, TODOS los consumidores re-se-renderizan, aunque solo necesiten parte del valor. Si compartís el state global de tu app en un solo Context, un cambio en cualquier propiedad causa re-render de toda la app. Eso es una bomba de rendimiento.

Soluciones:

  • Dividí Contexts por dominio: AuthContext para auth, UIContext para temas/modales, DataContext para datos. Así un cambio en tema no dispara re-render de componentes de datos.
  • Usá custom hooks para selectores: en vez de consumir todo el Context, creá hooks que extraen solo lo que necesitás.
  • Considerá librerías especializadas: Zustand, Redux, Recoil manejan updates granulares mejor. Si tu app es simple, Context alcanza. Si es compleja, especializa.

El pattern común de error: meter TODO en un Context global, App state, user state, UI state, feature flags. Un cambio en feature flags hace re-render de toda la app. Mala idea.

React Compiler y Server Components: 2026

React 19.2+ trae React Compiler, una herramienta que automáticamente memoiza dependencias. Vos escribís código simple, Compiler inserta useMemo y useCallback donde necesitás. Es como tener un botón que optimiza todo automáticamente. Todavía está en desarrollo pero es el futuro: menos trabajo manual, más optimizaciones.

Server Components en Next.js van más lejos: el componente renderiza en el servidor, no en el cliente. Solo la salida HTML llega al navegador. Beneficio: reducís JS del cliente dramáticamente. Una página que pesa 200KB de JS del cliente puede pesar 40KB con Server Components. El tradeoff: menos interactividad real-time en ciertos casos, pero para la mayoría de casos es un win. En herramientas con IA para mejorar desarrollo profundizamos sobre esto.

Tabla comparativa: técnicas de optimización y cuándo usarlas

TécnicaCuándo usarlaImpacto esperadoComplejidad
React.memoComponentes costosos con props establesReduce re-renders innecesarios 30-50%Baja
useMemo / useCallbackValores/funciones costosas, dependencias clarasReduce cálculos repetidos 20-40%Media
Code splittingApps medianas/grandes con múltiples rutasReduce JS inicial 40-60%Baja (automático en Next.js)
VirtualizaciónListas/tablas con 100+ itemsRenderizado O(1), fluido siempreMedia
Context splittingApps con state global frecuenteReduce cascadas de re-render 30-70%Media
Server ComponentsContenido estático o generado en servidorReduce JS cliente 60-80%Alta (requiere Next.js)
optimizar react diagrama explicativo

Errores comunes que cometés (sin saberlo)

Error 1: Memoizar todo por si acaso. Desarrolladores juniors meten React.memo en todos lados pensando que es magia. La verdad es que React.memo cuesta CPU. Si no hay re-render innecesario, no hay problema que solucionar. Medí primero.

Error 2: Asumir que un estado global es un problema sin verificar. “Mi app es lenta, seguro es porque uso Context global.” A veces la culpa es una query pesada, un loop en renderizado, o un plugin de navegador. Profilea antes de refactorizar toda tu arquitectura de estado.

Error 3: No usar code splitting en rutas.** Tenés 50 páginas en un SPA y un solo bundle de 2MB. El usuario que solo necesita ver 3 páginas descarga 47 páginas que nunca va a visitar. Code splitting es casi gratis en Next.js y webpack.

Error 4: Crear derivados costosos en render.** Calcular filtros, ordenamientos, maps gigantes dentro del render cada vez que pasa. Memoizá eso o movelo fuera.

Checklist práctico: cómo aplicar optimización ahora

Paso 1: Medir. Abrí React DevTools Profiler, grabá una interacción de usuario, identificá el componente que tardó más de 20ms. Ese es tu candidato. Cubrimos ese tema en detalle en repositorios y control de versiones modernos.

Paso 2: Entendé por qué se re-renderizó. ¿Cambió la prop? ¿Cambió el estado del padre? ¿Es un re-render en cascada desde Context? La causa sugiere la solución.

Paso 3: Aplicá la técnica específica. Si el problema es props que cambian constantemente, React.memo + useCallback. Si es estado global, dividí Contexts. Si es bundle grande, code split. Si es lista larga, virtualiza.

Paso 4: Verificá el impacto. Grabá de nuevo con Profiler. ¿Bajó el tiempo? ¿20ms → 8ms? Bueno. ¿Sigue en 20ms? La técnica no era la adecuada, intent otra.

Prioridad de optimización por impacto esperado:

  • Code splitting: 40-60% reducción en JS inicial (máximo impacto en LCP)
  • Virtualización: O(n) → O(1) en listas grandes (máximo impacto en fluidez)
  • Context splitting: 30-70% reducción en cascadas (impacto en tiempos de interacción)
  • React.memo + useCallback: 20-50% reducción en renders (impacto moderado, depende del caso)

Preguntas frecuentes

¿React.memo es igual a PureComponent?

Funciona similar pero React.memo es para funcionales, PureComponent para clases. React.memo compara props superficialmente por defecto (Object.is). Si necesitás comparación personalizada, pasá tu propio areEqual.

¿Cuál es el límite de tamaño de bundle antes de que sea un problema?

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

La regla es: usá React.memo solo si:

  • El componente recibe props complejas que no cambian frecuentemente
  • El componente es caro de renderizar (cálculos pesados, mucho DOM)
  • Verificaste con Profiler que efectivamente se re-renderiza innecesariamente

useMemo y useCallback son hooks para memoizar valores y funciones dentro del componente. Ojo: estos también cuestan. Memoizar tiene un costo de comparación. Si tu función es simple, el costo de memoizar puede ser mayor que el beneficio de no recrearla. Según análisis 2025, memoizar agresivamente sin medir puede empeorar el rendimiento porque constantemente comparás referencias en memoria.

El escenario donde memoización es un golazo: una lista de componentes hijo donde cada uno hace un cálculo costoso, recibe una callback como prop, y el padre se re-renderiza frecuentemente. Memoizá los hijos y la callback, y verás mejoras reales.

Code splitting y lazy loading con React.lazy()

Tu bundle JavaScript inicial es todo lo que el navegador descarga antes de renderizar. Si metés todo (Dashboard, Settings, AdminPanel, etc.) en un solo bundle, los usuarios esperan a que baje y parsee 500KB de JS solo para ver la landing page (spoiler: no es ideal). Code splitting divide el bundle en chunks. El usuario baja solo lo que necesita.

React.lazy() y Suspense lo hacen simple:

const AdminPanel = React.lazy(() => import('./AdminPanel'));

Cuando el usuario navega a /admin, React carga dinamicamente el chunk de AdminPanel. Mientras se descarga, mostrás un Suspense fallback (spinner, esqueleto). Según benchmarks 2025, code splitting puede reducir el JS inicial entre 40-60%. Si tu app actual pesa 800KB y dividís en 5 chunks, el usuario baja 160KB inicialmente. El impacto en LCP es directo.

Herramientas como webpack (que Next.js usa) hacen esto automáticamente si usás dynamic imports. Si estás en Next.js, ya pasa: cada página es un chunk separado.

Virtualización de listas: renderizar solo lo visible

Tenés una tabla de 10.000 items. Cada item es un componente con cálculos, eventos, estado local. Si renderizás todos los 10.000, la pestaña se congela. El viewport solo muestra 20. ¿Por qué renderizar los otros 9.980? Complementá con consideraciones de seguridad en tu código.

Virtualización renderiza solo los items dentro del viewport más un buffer. Cuando scrolleás, renderiza los nuevos y destruye los que salieron. Es un cambio de paradigma de O(n) renders a O(1). Librerías como react-window (recomendada en 2025) manejan el cálculo automáticamente. Vos le pasás el largo total, la altura del item, y una función que renderiza cada item por índice. Virtualización cuida el resto.

Caso real: un feed infinito de redes sociales con 5.000+ posts. Sin virtualización, después de scrollear un rato la app explota. Con virtualización, podés scrollear infinitamente sin lag. El cambio de usuario final es dramático.

Gestión de estado: Context API y sus límites

Context API es fácil de usar pero tiene una trampa: cuando cambia un valor en el Context, TODOS los consumidores re-se-renderizan, aunque solo necesiten parte del valor. Si compartís el state global de tu app en un solo Context, un cambio en cualquier propiedad causa re-render de toda la app. Eso es una bomba de rendimiento.

Soluciones:

  • Dividí Contexts por dominio: AuthContext para auth, UIContext para temas/modales, DataContext para datos. Así un cambio en tema no dispara re-render de componentes de datos.
  • Usá custom hooks para selectores: en vez de consumir todo el Context, creá hooks que extraen solo lo que necesitás.
  • Considerá librerías especializadas: Zustand, Redux, Recoil manejan updates granulares mejor. Si tu app es simple, Context alcanza. Si es compleja, especializa.

El pattern común de error: meter TODO en un Context global, App state, user state, UI state, feature flags. Un cambio en feature flags hace re-render de toda la app. Mala idea.

React Compiler y Server Components: 2026

React 19.2+ trae React Compiler, una herramienta que automáticamente memoiza dependencias. Vos escribís código simple, Compiler inserta useMemo y useCallback donde necesitás. Es como tener un botón que optimiza todo automáticamente. Todavía está en desarrollo pero es el futuro: menos trabajo manual, más optimizaciones.

Server Components en Next.js van más lejos: el componente renderiza en el servidor, no en el cliente. Solo la salida HTML llega al navegador. Beneficio: reducís JS del cliente dramáticamente. Una página que pesa 200KB de JS del cliente puede pesar 40KB con Server Components. El tradeoff: menos interactividad real-time en ciertos casos, pero para la mayoría de casos es un win. En herramientas con IA para mejorar desarrollo profundizamos sobre esto.

Tabla comparativa: técnicas de optimización y cuándo usarlas

TécnicaCuándo usarlaImpacto esperadoComplejidad
React.memoComponentes costosos con props establesReduce re-renders innecesarios 30-50%Baja
useMemo / useCallbackValores/funciones costosas, dependencias clarasReduce cálculos repetidos 20-40%Media
Code splittingApps medianas/grandes con múltiples rutasReduce JS inicial 40-60%Baja (automático en Next.js)
VirtualizaciónListas/tablas con 100+ itemsRenderizado O(1), fluido siempreMedia
Context splittingApps con state global frecuenteReduce cascadas de re-render 30-70%Media
Server ComponentsContenido estático o generado en servidorReduce JS cliente 60-80%Alta (requiere Next.js)
optimizar react diagrama explicativo

Errores comunes que cometés (sin saberlo)

Error 1: Memoizar todo por si acaso. Desarrolladores juniors meten React.memo en todos lados pensando que es magia. La verdad es que React.memo cuesta CPU. Si no hay re-render innecesario, no hay problema que solucionar. Medí primero.

Error 2: Asumir que un estado global es un problema sin verificar. “Mi app es lenta, seguro es porque uso Context global.” A veces la culpa es una query pesada, un loop en renderizado, o un plugin de navegador. Profilea antes de refactorizar toda tu arquitectura de estado.

Error 3: No usar code splitting en rutas.** Tenés 50 páginas en un SPA y un solo bundle de 2MB. El usuario que solo necesita ver 3 páginas descarga 47 páginas que nunca va a visitar. Code splitting es casi gratis en Next.js y webpack.

Error 4: Crear derivados costosos en render.** Calcular filtros, ordenamientos, maps gigantes dentro del render cada vez que pasa. Memoizá eso o movelo fuera.

Checklist práctico: cómo aplicar optimización ahora

Paso 1: Medir. Abrí React DevTools Profiler, grabá una interacción de usuario, identificá el componente que tardó más de 20ms. Ese es tu candidato. Cubrimos ese tema en detalle en repositorios y control de versiones modernos.

Paso 2: Entendé por qué se re-renderizó. ¿Cambió la prop? ¿Cambió el estado del padre? ¿Es un re-render en cascada desde Context? La causa sugiere la solución.

Paso 3: Aplicá la técnica específica. Si el problema es props que cambian constantemente, React.memo + useCallback. Si es estado global, dividí Contexts. Si es bundle grande, code split. Si es lista larga, virtualiza.

Paso 4: Verificá el impacto. Grabá de nuevo con Profiler. ¿Bajó el tiempo? ¿20ms → 8ms? Bueno. ¿Sigue en 20ms? La técnica no era la adecuada, intent otra.

Prioridad de optimización por impacto esperado:

  • Code splitting: 40-60% reducción en JS inicial (máximo impacto en LCP)
  • Virtualización: O(n) → O(1) en listas grandes (máximo impacto en fluidez)
  • Context splitting: 30-70% reducción en cascadas (impacto en tiempos de interacción)
  • React.memo + useCallback: 20-50% reducción en renders (impacto moderado, depende del caso)

Preguntas frecuentes

¿React.memo es igual a PureComponent?

Funciona similar pero React.memo es para funcionales, PureComponent para clases. React.memo compara props superficialmente por defecto (Object.is). Si necesitás comparación personalizada, pasá tu propio areEqual.

¿Cuál es el límite de tamaño de bundle antes de que sea un problema?

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Optimizar React es más que aplicar trucos al azar: requiere medir dónde está el cuello de botella, aplicar la técnica correcta para ese problema específico, y verificar que efectivamente mejoró. Un desarrollador reportó mejoras del 67% en rendimiento usando React DevTools Profiler para identificar re-renders innecesarios, luego aplicó memoización selectiva, code splitting y gestión inteligente de estado. No fue suerte: fue diagnóstico sistemático.

En 30 segundos

  • Medí primero: React DevTools Profiler te muestra dónde están los re-renders innecesarios antes de que empieces a optimizar.
  • Memoizá selectivamente: React.memo, useMemo y useCallback funcionan solo si evitás re-renders costosos reales, no para todo.
  • Code splitting reduce el JS inicial en un 40-60% cargando componentes bajo demanda con React.lazy() y Suspense.
  • Virtualización de listas convierte O(n) renders en O(1) cuando trabajás con miles de items: librerías como react-window lo hacen sencillo.
  • React 19.2 Compiler y Server Components en Next.js automatizan optimizaciones que antes hacías manualmente.

React es una librería para construir interfaces, pero renderizar componentes constantemente es caro. La optimización de React es la práctica de reducir renders innecesarios, dividir el código en partes más pequeñas, y estructurar el estado para que solo las partes que cambian se re-rendericen.

Medir antes de optimizar: React DevTools Profiler

Ponele que tu app React es lenta. Antes de saltar a agregar useMemo en todas partes, necesitás saber dónde está el problema. Abrir Chrome DevTools Performance tab y grabar un trace te da datos brutos, pero es tedioso interpretar. React DevTools Profiler, en cambio, te muestra exactamente qué componentes se renderizan, cuánto tardo cada uno, y por qué se re-renderizó. Sin eso, estás optimizando a ciegas.

Instalá React DevTools como extensión de Chrome. Entrá a la pestaña Profiler, iniciá una grabación, interactuá con tu app, y detené. La herramienta te muestra un gráfico de llamadas: cuáles componentes se rendered, en qué orden, cuántos ms tardó cada uno. Si un componente tardó 500ms innecesariamente, lo ves. Si se re-renderizó 50 veces cuando solo necesitaba 2, lo ves. Eso es diagnóstico real.

Web Vitals también importa. Core Web Vitals (LCP, INP, CLS) son las métricas que Google usa para ranking. LCP es cuánto tarda en renderizar el contenido más grande. Si tu bundle es de 500KB y no hacés code splitting, el LCP sufre. Chrome DevTools te dice si estás mal en estos números. Medir es el primer paso.

Memoización: React.memo, useMemo y useCallback

React.memo es un wrapper para componentes funcionales que previene re-renders si las props no cambian. Suena perfecto, pero acá viene la verdad incómoda: React.memo no es un free lunch. La comparación de props también cuesta. Si usas React.memo en un componente que se re-renderiza igual que sus padres (porque las props siempre cambian), gastaste CPU en comparación sin beneficio. Ya lo cubrimos antes en ejecutar herramientas locales sin APIs externas.

La regla es: usá React.memo solo si:

  • El componente recibe props complejas que no cambian frecuentemente
  • El componente es caro de renderizar (cálculos pesados, mucho DOM)
  • Verificaste con Profiler que efectivamente se re-renderiza innecesariamente

useMemo y useCallback son hooks para memoizar valores y funciones dentro del componente. Ojo: estos también cuestan. Memoizar tiene un costo de comparación. Si tu función es simple, el costo de memoizar puede ser mayor que el beneficio de no recrearla. Según análisis 2025, memoizar agresivamente sin medir puede empeorar el rendimiento porque constantemente comparás referencias en memoria.

El escenario donde memoización es un golazo: una lista de componentes hijo donde cada uno hace un cálculo costoso, recibe una callback como prop, y el padre se re-renderiza frecuentemente. Memoizá los hijos y la callback, y verás mejoras reales.

Code splitting y lazy loading con React.lazy()

Tu bundle JavaScript inicial es todo lo que el navegador descarga antes de renderizar. Si metés todo (Dashboard, Settings, AdminPanel, etc.) en un solo bundle, los usuarios esperan a que baje y parsee 500KB de JS solo para ver la landing page (spoiler: no es ideal). Code splitting divide el bundle en chunks. El usuario baja solo lo que necesita.

React.lazy() y Suspense lo hacen simple:

const AdminPanel = React.lazy(() => import('./AdminPanel'));

Cuando el usuario navega a /admin, React carga dinamicamente el chunk de AdminPanel. Mientras se descarga, mostrás un Suspense fallback (spinner, esqueleto). Según benchmarks 2025, code splitting puede reducir el JS inicial entre 40-60%. Si tu app actual pesa 800KB y dividís en 5 chunks, el usuario baja 160KB inicialmente. El impacto en LCP es directo.

Herramientas como webpack (que Next.js usa) hacen esto automáticamente si usás dynamic imports. Si estás en Next.js, ya pasa: cada página es un chunk separado.

Virtualización de listas: renderizar solo lo visible

Tenés una tabla de 10.000 items. Cada item es un componente con cálculos, eventos, estado local. Si renderizás todos los 10.000, la pestaña se congela. El viewport solo muestra 20. ¿Por qué renderizar los otros 9.980? Complementá con consideraciones de seguridad en tu código.

Virtualización renderiza solo los items dentro del viewport más un buffer. Cuando scrolleás, renderiza los nuevos y destruye los que salieron. Es un cambio de paradigma de O(n) renders a O(1). Librerías como react-window (recomendada en 2025) manejan el cálculo automáticamente. Vos le pasás el largo total, la altura del item, y una función que renderiza cada item por índice. Virtualización cuida el resto.

Caso real: un feed infinito de redes sociales con 5.000+ posts. Sin virtualización, después de scrollear un rato la app explota. Con virtualización, podés scrollear infinitamente sin lag. El cambio de usuario final es dramático.

Gestión de estado: Context API y sus límites

Context API es fácil de usar pero tiene una trampa: cuando cambia un valor en el Context, TODOS los consumidores re-se-renderizan, aunque solo necesiten parte del valor. Si compartís el state global de tu app en un solo Context, un cambio en cualquier propiedad causa re-render de toda la app. Eso es una bomba de rendimiento.

Soluciones:

  • Dividí Contexts por dominio: AuthContext para auth, UIContext para temas/modales, DataContext para datos. Así un cambio en tema no dispara re-render de componentes de datos.
  • Usá custom hooks para selectores: en vez de consumir todo el Context, creá hooks que extraen solo lo que necesitás.
  • Considerá librerías especializadas: Zustand, Redux, Recoil manejan updates granulares mejor. Si tu app es simple, Context alcanza. Si es compleja, especializa.

El pattern común de error: meter TODO en un Context global, App state, user state, UI state, feature flags. Un cambio en feature flags hace re-render de toda la app. Mala idea.

React Compiler y Server Components: 2026

React 19.2+ trae React Compiler, una herramienta que automáticamente memoiza dependencias. Vos escribís código simple, Compiler inserta useMemo y useCallback donde necesitás. Es como tener un botón que optimiza todo automáticamente. Todavía está en desarrollo pero es el futuro: menos trabajo manual, más optimizaciones.

Server Components en Next.js van más lejos: el componente renderiza en el servidor, no en el cliente. Solo la salida HTML llega al navegador. Beneficio: reducís JS del cliente dramáticamente. Una página que pesa 200KB de JS del cliente puede pesar 40KB con Server Components. El tradeoff: menos interactividad real-time en ciertos casos, pero para la mayoría de casos es un win. En herramientas con IA para mejorar desarrollo profundizamos sobre esto.

Tabla comparativa: técnicas de optimización y cuándo usarlas

TécnicaCuándo usarlaImpacto esperadoComplejidad
React.memoComponentes costosos con props establesReduce re-renders innecesarios 30-50%Baja
useMemo / useCallbackValores/funciones costosas, dependencias clarasReduce cálculos repetidos 20-40%Media
Code splittingApps medianas/grandes con múltiples rutasReduce JS inicial 40-60%Baja (automático en Next.js)
VirtualizaciónListas/tablas con 100+ itemsRenderizado O(1), fluido siempreMedia
Context splittingApps con state global frecuenteReduce cascadas de re-render 30-70%Media
Server ComponentsContenido estático o generado en servidorReduce JS cliente 60-80%Alta (requiere Next.js)
optimizar react diagrama explicativo

Errores comunes que cometés (sin saberlo)

Error 1: Memoizar todo por si acaso. Desarrolladores juniors meten React.memo en todos lados pensando que es magia. La verdad es que React.memo cuesta CPU. Si no hay re-render innecesario, no hay problema que solucionar. Medí primero.

Error 2: Asumir que un estado global es un problema sin verificar. “Mi app es lenta, seguro es porque uso Context global.” A veces la culpa es una query pesada, un loop en renderizado, o un plugin de navegador. Profilea antes de refactorizar toda tu arquitectura de estado.

Error 3: No usar code splitting en rutas.** Tenés 50 páginas en un SPA y un solo bundle de 2MB. El usuario que solo necesita ver 3 páginas descarga 47 páginas que nunca va a visitar. Code splitting es casi gratis en Next.js y webpack.

Error 4: Crear derivados costosos en render.** Calcular filtros, ordenamientos, maps gigantes dentro del render cada vez que pasa. Memoizá eso o movelo fuera.

Checklist práctico: cómo aplicar optimización ahora

Paso 1: Medir. Abrí React DevTools Profiler, grabá una interacción de usuario, identificá el componente que tardó más de 20ms. Ese es tu candidato. Cubrimos ese tema en detalle en repositorios y control de versiones modernos.

Paso 2: Entendé por qué se re-renderizó. ¿Cambió la prop? ¿Cambió el estado del padre? ¿Es un re-render en cascada desde Context? La causa sugiere la solución.

Paso 3: Aplicá la técnica específica. Si el problema es props que cambian constantemente, React.memo + useCallback. Si es estado global, dividí Contexts. Si es bundle grande, code split. Si es lista larga, virtualiza.

Paso 4: Verificá el impacto. Grabá de nuevo con Profiler. ¿Bajó el tiempo? ¿20ms → 8ms? Bueno. ¿Sigue en 20ms? La técnica no era la adecuada, intent otra.

Prioridad de optimización por impacto esperado:

  • Code splitting: 40-60% reducción en JS inicial (máximo impacto en LCP)
  • Virtualización: O(n) → O(1) en listas grandes (máximo impacto en fluidez)
  • Context splitting: 30-70% reducción en cascadas (impacto en tiempos de interacción)
  • React.memo + useCallback: 20-50% reducción en renders (impacto moderado, depende del caso)

Preguntas frecuentes

¿React.memo es igual a PureComponent?

Funciona similar pero React.memo es para funcionales, PureComponent para clases. React.memo compara props superficialmente por defecto (Object.is). Si necesitás comparación personalizada, pasá tu propio areEqual.

¿Cuál es el límite de tamaño de bundle antes de que sea un problema?

Depende de tu audiencia. Para móvil 3G (todavía común en LATAM), 200KB+ de JS es problemático: tarda más de 5 segundos en descargar. Para conexiones más rápidas, podés llegar a 500KB sin dolor. Pero si podés reducir a 100KB con code splitting, mejor. Core Web Vitals mejora si LCP < 2.5s.

¿Redux es mejor que Context para estado global?

Redux tiene boilerplate pero es más predecible y recomendable para apps grandes. Context es más simple para apps medianas. Zustand es un punto medio: React hooks + estado global sin boilerplate. Elijo según tamaño: pequeño → Context, mediano → Zustand, grande → Redux.

¿Web Workers pueden ayudar en optimización?

Sí, si tenés cálculos pesados (procesamiento de imágenes, análisis de datos). Movés el trabajo a un worker thread, el main thread sigue libre para rendering. No es típicamente necesario para optimización React básica pero es una herramienta avanzada útil.

¿Debo usar Suspense para todo lazy loading?

Conclusión

Fuentes

Te puede interesar...