Product Craft Bible
Performance Percibida
Inicio Avanzado Performance Percibida
Avanzado

Performance Percibida

8 reglas chrome developers · nielsen normannielsen norman · smashing magazinesmashing magazine · nielsen normanchrome developers · mdn
173

Performance Percibida

8 reglas
1508

Skeleton screens en lugar de spinners (>1s)

Cuando la carga tarda más de 1 segundo, un skeleton screen mantiene la percepción de progreso mejor que un spinner. El skeleton reserva el espacio de la UI real, eliminando el salto de layout cuando el contenido llega.

chrome developers · nielsen norman
Preferir
Evitar
Cargando...
El usuario no tiene idea de que estructura va a aparecer
1509

No skeleton en elementos pequeños ni formularios

Los skeletons tienen sentido en tarjetas, listas y feeds. En botones, inputs, labels o modales resultan distrayentes y confusos. Usar spinner puntual o simplemente deshabilitar el elemento mientras carga.

nielsen norman · smashing magazine
Preferir
Iniciar sesión
Transacciones recientes
Formulario renderizado al instante, skeleton solo en la sección de datos que tarda en cargar
Evitar
Iniciar sesión
Skeleton en labels, inputs, toggles y badges: ruido visual que parece roto, no rápido
1510

Optimistic UI en acciones frecuentes de bajo riesgo

En acciones que raramente fallan (dar like, marcar como leido, reordenar), aplicar el cambio visualmente antes de que el servidor responda. Si falla, revertir con una notificación discreta. Elimina la latencia percibida en el flujo principal del usuario.

smashing magazine · nielsen norman
Preferir
Post de equipo
Gran avance en el sprint de esta semana. El equipo logro cerrar 14 tickets.
25
3
Click → UI actualiza al instante (0ms)
Llamada al servidor en background
Servidor confirma → sin cambio visible
Si falla → revertir + toast de error sutil
Feedback instantaneo, sin latencia percibida
El corazón se llena al instante. Si el servidor falla, se revierte con un toast discreto. 0ms de latencia percibida.
Evitar
Post de equipo
Gran avance en el sprint de esta semana. El equipo logro cerrar 14 tickets.
Enviando...
24
3
Esperando respuesta del servidor... 1-2s de delay visible
El usuario da like y ve un spinner en el botón. 1-2 segundos de espera para una acción trivial que se siente lenta.
1511

Reservar dimensiones de imágenes para eliminar CLS

El Cumulative Layout Shift (CLS) penaliza la experiencia y el SEO. Siempre definir width y height en el HTML de la imagen, y reforzar con aspect-ratio en CSS para que el browser reserve el espacio antes de la descarga. Sin estas propiedades el contenido debajo de la imagen salta hacia abajo cuando el recurso termina de cargar, generando un CLS alto que degrada tanto la percepción del usuario como el ranking en Core Web Vitals.

chrome developers · mdn
Preferir
espacio reservado 4:3
Articulo reciente
El layout es estable desde el primer render. El contenido no se mueve.
CLS 0.02
<img width="400" height="300" style="aspect-ratio:4/3">
Evitar
sin width/height
Articulo reciente
Este texto salta hacia abajo cuando la imagen termina de cargar.
↓ contenido se desplaza
CLS 0.42
1512

Priorizar contenido central, diferir secundario (LCP)

El Largest Contentful Paint (LCP) debe estar bajo 2.5s. Cargar con fetchpriority="high" la imagen hero. Diferir con loading="lazy" todo lo que queda fuera del viewport inicial. Aplicar font-display: swap para evitar FOIT. Sin prioridad explicita, el browser descarga recursos en orden arbitrario: hero, sidebar ads, chat widget y analytics compiten por ancho de banda, retrasando el contenido que el usuario realmente ve. Con prioridad explicita, el hero llega primero y todo lo secundario se carga después o bajo demanda.

chrome developers · mdn
Preferir
example.com
Hero img
fetchpriority="high"
Ad lazy
Chat defer
Analytics dynamic import
Hero img
Critical CSS
Sidebar ads
Chat widget
Analytics
LCP: 1.1s
Hero primero, secundario diferido. Timeline escalonado.
Evitar
example.com
Hero img (sin fetchpriority)
Ad banner
Chat widget
Analytics scripts cargando...
Hero img
Sidebar ads
Chat widget
Analytics
LCP: 4.2s
Todo compite por ancho de banda al mismo tiempo
1513

Prefetch en hover para simular carga instantanea

El usuario tarda entre 200 y 300ms entre el hover y el click. Usar ese tiempo para precargar la siguiente página o recurso con <link rel="prefetch"> inyectado en el mouseenter. Cuando el click ocurre, los recursos ya están en cache y la página se muestra sin delay visible. El impacto es especialmente notorio en rutas de producto, checkout y navegación principal.

chrome developers · smashing magazine
Preferir
Con prefetch en hover: carga instantanea al click
hover 0ms
prefetch ~200ms
click 300ms
página (~0ms)
Recursos ya en cache, navegación instantanea
<link rel="prefetch"> inyectado en mouseenter
Evitar
Sin prefetch: el usuario espera después del click
hover
click
cargando ~800ms
página
Spinner visible, 800ms+ de espera. Sin prefetch.
1514

INP por debajo de 200ms

Interaction to Next Paint (INP) mide el tiempo entre la interacción del usuario y el proximo frame pintado. Objetivo: <200ms (bueno), 200-500ms (mejora necesaria), >500ms (mala experiencia). Causas típicas: handlers sincronicos pesados, DOM updates excesivos, JavaScript bloqueante en el main thread. Diferir trabajo pesado con scheduler.yield(), requestIdleCallback o setTimeout(0) para que el browser pinte el feedback visual antes de ejecutar la lógica costosa.

chrome developers
Preferir
Main thread timeline
Guardar cambios
Feedback visual inmediato, trabajo pesado diferido
click
UI (48ms)
paint
requestIdleCallback / scheduler.yield
INP: 89ms Bueno
async function onClick() { updateButtonState(); // instant visual feedback await scheduler.yield(); heavyWork(); // runs after paint }
La UI responde a los 89ms con estado pressed visible, el trabajo costoso se difiere tras el paint
Evitar
Main thread timeline
Guardar cambios
Click con 400ms+ sin respuesta visual
click
JS sincrono pesado (412ms)
paint
INP: 412ms Malo
El main thread bloqueado 412ms: el botón no muestra estado pressed, el usuario siente la interfaz congelada
1515

Lazy loading nativo para imagenes below-the-fold

El atributo loading="lazy" es nativo en todos los browsers modernos. Aplicarlo a cualquier imagen que no sea visible en el viewport inicial. No necesita JavaScript ni librerias. Combinado con fetchpriority="high" en el hero, reduce el peso inicial de la página significativamente. Sin lazy loading, el browser descarga todas las imagenes al inicio aunque el usuario solo vea las primeras 2-3. Con lazy loading nativo, las imagenes below-the-fold se solicitan al acercarse al viewport, reduciendo el peso inicial hasta un 80% en páginas con galerias o listados.

mdn · chrome developers
Preferir
tienda.com/catalogo
viewport (visible)
img 1
img 2
img 3
below the fold (lazy)
lazy
lazy
lazy
lazy
lazy
lazy
img 1
img 2
img 3
img 4-20
380 KB initial
<img loading="lazy" src="galeria-4.webp" alt="...">
Above-fold eager, below-fold lazy. Carga escalonada al acercarse al viewport.
Evitar
tienda.com/catalogo
viewport (visible)
img 1
img 2
img 3
below the fold
img 4
img 5
img 6
img 7
img 8
img 9
img 10
img 11
img 12
img 13
img 14
img 15
img 16
img 17
img 18
img 19
img 20
2.4 MB initial payload
20 imagenes se descargan al inicio aunque solo 3 son visibles. Payload inicial pesado e innecesario.