React para quien tiene un celular básico: lección de un relato en HN
Alguien publicó en Hacker News que lleva diez años pagando los estudios de un joven de la zona rural de Tanzania. El post se llama "Tell HN: I've been paying for a rural Tanzanian's education for 10 years". No es un lanzamiento, ni un framework, ni unas release notes. Aun así, me hizo abrir el proyecto en React en el que estoy trabajando ahora y hacerme una pregunta incómoda: ¿esta app funcionaría en las manos de quien está del otro lado de esa historia?
Este blog suele comentar noticias técnicas. Esta es humana. Pero todo relato así esconde una pregunta técnica. Cuando alguien sale de la zona rural y llega a la escuela, a la universidad o a su primer empleo, el acceso a internet pasa por una pantalla. Casi siempre es un Android de gama baja, con red inestable y datos contados. Y buena parte de esa pantalla hoy está hecha con React.
¿Qué tiene que ver el relato con React?
Directamente, nada. No leí en ningún lado que el post hable de código. La conexión es otra.
Piensa en el camino de alguien que estudia en una región rural de África Oriental. La inscripción, el material, el resultado del examen, la beca, la oferta de trabajo: cada vez más, todo eso se vuelve un formulario web. Y quienes mantenemos esos formularios somos nosotros.
Nosotros desarrollamos con un MacBook nuevo, fibra de 500 megas y Chrome con la caché caliente. El usuario real abre el mismo sitio en un equipo con un cuarto de tu CPU, en un 3G que se cae en cada túnel, pagando por megabyte.
Tu usuario más importante casi nunca tiene tu máquina.
Eso es lo que el relato me recordó. Diez años pagando la educación de alguien es un compromiso largo y constante. La versión dev de eso es mucho más modesta: no dejar la puerta demasiado pesada para quien intenta entrar.
Cuánto pesa una app React en un celular barato
Vamos a los números que puedes comprobar tú mismo.
El par react + react-dom en producción ronda los 60 KB comprimidos con gzip. Parece poco. El problema es que nadie entrega solo eso. Suma el router, una librería de formularios, una de fechas, un kit de componentes, un SDK de analytics y un chat de soporte. No es raro que una app "sencilla" mande entre 500 KB y 1 MB de JavaScript comprimido en la primera carga.
En JavaScript, el tamaño de la descarga es solo la mitad de la cuenta. El navegador todavía tiene que descomprimir, hacer el parse, compilar y ejecutar. En un equipo de gama baja, esa parte puede tardar varias veces más que en tu notebook. He visto pantallas de login congeladas durante segundos en un Android viejo mientras el bundle terminaba de ejecutarse. Sin ningún error en la consola. Solo un usuario mirando un botón que no responde.
Para el usuario, esto aparece de tres formas:
- Pantalla en blanco durante mucho tiempo antes del primer contenido.
- Toques que no responden mientras la hidratación no termina.
- Datos del plan gastados en código que ni siquiera va a usar en esa visita.
Cómo probar tu app como si estuvieras allí
No necesitas viajar ni comprar un celular de 400 reales para hacerte una idea. Necesitas cinco minutos en DevTools.
- Abre Chrome DevTools, pestaña Performance.
- En CPU, elige 6x slowdown.
- En Network, elige Slow 4G o crea un perfil más lento.
- Marca Disable cache y recarga.
- Graba y mira cuánto tiempo pasa el hilo principal ocupado antes de que la página acepte un clic.
Hazlo en tu pantalla más importante. Puede ser el login, el checkout o el formulario de registro. La primera vez da un poco de vergüenza. La segunda, dan ganas de abrir un PR.
Si tienes un Android viejo en un cajón, mejor todavía. Activa el USB debugging, abre chrome://inspect y prueba en el equipo de verdad. Ninguna simulación reemplaza al equipo real calentándose en tu mano.
Qué cambiar en el código hoy
No hace falta reescribir nada ni cambiar de framework. La mayor parte de la mejora viene de ajustes aburridos y baratos.
Carga bajo demanda lo que no aparece en la primera pantalla. Gráficos, editor de texto enriquecido, mapa, modal de configuración: nada de eso tiene que estar en el bundle inicial.
import { lazy, Suspense } from 'react'
const InformeChart = lazy(() => import('./InformeChart'))
export function Panel() {
return (
<Suspense fallback={<p>Cargando informe…</p>}>
<InformeChart />
</Suspense>
)
}
Manda menos JavaScript, no solo JavaScript más pequeño. Si usas Next.js con App Router, los Server Components resuelven mucho. Una lista de cursos que solo muestra datos no tiene por qué convertirse en código en el cliente. Deja 'use client' solo en las partes que realmente tienen interacción.
Mira lo que entró en el bundle. Ejecuta un analizador una vez y busca las sorpresas de siempre: una librería de fechas entera para formatear un día, íconos importados en masa, un polyfill que ningún navegador actual necesita.
bunx vite-bundle-visualizer
# o, en Next.js
ANALYZE=true bun run build
Pon un límite y deja que el CI lo exija. Una regla simple evita que el bundle engorde 20 KB por sprint sin que nadie lo note. Con size-limit, por ejemplo:
{
"size-limit": [
{ "path": "dist/assets/index-*.js", "limit": "170 KB" }
]
}
El número exacto importa menos que tener un número. Sin límite, el bundle solo crece.
Formularios que sobreviven a una mala red. Guarda el borrador en localStorage en cada cambio. Quien llena una inscripción larga y lo pierde todo porque el 3G se cayó en el último campo rara vez lo vuelve a intentar.
const [form, setForm] = useState(() =>
JSON.parse(localStorage.getItem('inscripcion') ?? '{}')
)
useEffect(() => {
localStorage.setItem('inscripcion', JSON.stringify(form))
}, [form])
Son seis líneas. Para alguien con un plan prepago, esas seis líneas pueden decidir si la inscripción se envía o no.
"Pero mi público no está en Tanzania"
La objeción es justa. Y la respuesta es: quizás esté más cerca de lo que crees.
En Brasil, mucha gente se conecta a internet solo desde el celular. Y una gran parte de eso ocurre en equipos de gama baja y con planes prepago. Clientes de apps bancarias, alumnos de educación a distancia, repartidores mirando la ruta, gente del interior con una sola barrita de señal. Si tu producto atiende a "Brasil", también atiende a ese público. Lo pruebes para él o no.
Y hay un efecto secundario bueno: todo lo que hace la app ligera en un equipo básico también la hace más rápida en el iPhone de tu jefe. Optimizar para el peor caso nunca perjudica al mejor caso. Lo contrario pasa todo el tiempo.
Mi opinión, sin romantizar
No voy a fingir que optimizar un bundle es caridad. No lo es. La persona del relato hace algo mucho más grande que cualquier PR que yo vaya a abrir este año. Comparar las dos cosas sería ridículo, como creer que quitar moment.js del proyecto me hace candidato al Nobel.
Pero el post me recordó algo que olvidamos con facilidad. Del otro lado de la pantalla hay alguien con menos recursos que tú intentando terminar una tarea. A veces es una inscripción, a veces una oferta de trabajo. React no tiene la culpa de nada. Solo obedece lo que escribimos.
Mi sugerencia práctica es pequeña: elige una pantalla crítica de tu app, pruébala esta semana con la CPU 6x más lenta y una red mala, y corrige lo peor que aparezca. Con una sola cosa ya ayudas.
Si quieres ver cómo vengo aplicando esto en mis proyectos, están aquí.
Resumen para LinkedIn
Un formulario de inscripción pesado puede dejar fuera a alguien con un celular básico. Leí en Hacker News el relato de alguien que lleva 10 años pagando los estudios de un joven de la zona rural de Tanzania. Me quedé con una pregunta: ¿mi app React funcionaría en sus manos? Programamos con un MacBook nuevo y fibra. El usuario real usa un Android de gama baja, con 3G inestable y plan prepago. En Brasil pasa lo mismo: mucha gente solo se conecta a internet desde el celular, en un equipo barato. Bastan 5 minutos en DevTools: CPU 6x más lenta, Slow 4G y caché desactivada. Después, lazy loading, límite de bundle en el CI y borrador guardado en localStorage. Elige una pantalla crítica de tu app, pruébala así esta semana y corrige lo peor que aparezca. Escribí el paso a paso en el blog, con código. #React #RendimientoWeb #Frontend #Accesibilidad #DesarrolloWeb