Omitir la renderización fuera de pantalla con content-visibility y contain-intrinsic-size
Aprenda cómo la visibilidad del contenido: auto reduce el costo de diseño en páginas largas generadas por servidor, por qué es obligatorio utilizar contain-intrinsic-size, y cómo se compara con la virtualización.
Las páginas largas llenas de elementos repetitivos, como listados de productos, feeds, archivos y tablas grandes, suelen parecer lentas incluso cuando su JavaScript es pequeño. La razón suele ser que el navegador calcula los estilos y el diseño para cada elemento de la página, incluidos cientos de tarjetas a las que el visitante no ha desplazado la vista y posiblemente nunca llegará. Esta guía muestra cómo dos propiedades CSS, content-visibility y contain-intrinsic-size, permiten al navegador posponer ese trabajo, cómo este cambio se relaciona con Core Web Vitals y el indexado, y dónde esta técnica falla o es la herramienta inadecuada.
Un caso típico: una lista larga que es lenta sin motivo aparente
Imagínese una página de categoría “Ver todos los productos” para una tienda en línea. Aproximadamente 600 tarjetas de productos, cada una con una imagen, un título, un precio y una calificación, se generan en el servidor para formar una sola página extensa. No hay paginación, ni desplazamiento infinito, ni virtualización. A la empresa le gusta así: una única URL, todos los productos accesibles, y todo alcanzable mediante la función de búsqueda de la página del navegador.
En una laptop de gama media, la página tarda casi cuatro segundos en volverse interactiva. El principal sospechoso es JavaScript: un paquete excesivamente grande, un useEffect que no funciona correctamente, o un componente que se vuelve a renderizar en bucle. Un examen detallado del paquete no revela nada que explique la demora.
El panel de rendimiento en DevTools cuenta una historia diferente. La mayor parte del tiempo del hilo principal se destina a Layout, y esto ocurre antes de que cualquier script tenga la oportunidad de influir en el rendimiento.
Por qué el contenido fuera de la pantalla sigue costándole dinero
El procesamiento no es una operación única. El navegador primero resuelve los estilos de cada elemento, luego ejecuta el diseño para determinar el tamaño y la posición de cada cuadro, y solo después pinta los píxeles. La pintura se limita en gran medida a lo que está dentro o cerca del área visible, pero la resolución de estilos y el diseño se realizan en todo el documento, incluido el contenido que está muy abajo. Para cuando el navegador decide qué pintar, el costoso trabajo de cálculo geométrico ya ha finalizado.
En el ejemplo de la tienda, eso significa que las 600 tarjetas, cada una con un cuadro de imagen, un título de envoltorio, un precio y una fila de calificación, pasan por los procesos de estilo y diseño antes de que el comprador vea la primera. El comprador probablemente vea ocho productos. Los otros 592 aún no son útiles, pero el navegador no tiene forma de saber si el visitante desplazará la pantalla hacia abajo, por lo que, por defecto, los trata a todos como contenido que debe estar listo de inmediato. Ese es el desperdicio que merece ser eliminado. Si desea repasar cómo difieren en costo estas etapas del proceso, consulte nuestro análisis de cuánto cuestan al navegador el reflow, la repintura y la composición.
La solución: dos declaraciones en el elemento repetido
Aplique ambas propiedades al elemento que se repite, en este caso la tarjeta del producto. La primera indica al navegador que puede omitir la renderización del contenido de la tarjeta cuando esta se encuentra lejos del área visible. La segunda proporciona un tamaño de marcador temporal para las tarjetas cuyo tamaño real aún no se conoce.
.product-card {
content-visibility: auto;
contain-intrinsic-size: auto 340px;
}
Con content-visibility: auto, una tarjeta que no está cerca del área visible permanece en el DOM y en el árbol de accesibilidad, por lo que find-in-page sigue localizando su texto. Lo que cambia es que el navegador no invierte esfuerzos en aplicar estilos, organizar su layout ni pintar su contenido hasta que la tarjeta se acerca a la pantalla. En realidad, esta propiedad aplica contención de layout, estilo y pintura al elemento, lo que permite al navegador tratar el subárbol como independiente y omitirlo de forma segura.
Qué tan grandes pueden ser las ventajas
En una demostración publicada en web.dev, Google tomó una página extensa dividida en secciones y redujo su tiempo de renderizado de 232 ms a 30 ms, lo que representa una mejora de aproximadamente siete veces. Cabe recordar que se trata de una página de demostración creada específicamente para este fin y no de un sitio en producción. En una lista de productos real como la descrita anteriormente, una mejora de alrededor de 2 veces es una expectativa más realista, y aún así puede marcar la diferencia entre una página que sigue cargándose y una que parece lista desde el primer dibujo.
Los documentos extremos muestran los límites máximos. En una presentación en el Chrome Dev Summit, esta propiedad se aplicó a una enorme especificación HTML de página única con más de 270,000 nodos DOM, y el tiempo de layout disminuyó de unos 50 segundos a aproximadamente 400 milisegundos. Pocas páginas se parecen a eso, pero ilustra cuánto esfuerzo se invierte en contenido que nadie puede ver.
Es útil ser preciso sobre lo que está sucediendo. CSS no hace que el procesador sea más rápido. Lo que haces es indicarle al navegador que una gran parte del trabajo no necesita realizarse en este momento. Cuando una página contiene 600 tarjetas y la vista muestra solo ocho, renderizar cada tarjeta de antemano rara vez es el mejor uso del hilo principal.
Por qué contain-intrinsic-size no es opcional
Si se utiliza solo content-visibility: auto, la barra de desplazamiento comienza a saltar al desplazarse, lo cual se puede confundir fácilmente con un error no relacionado.
La causa es sencilla: una tarjeta cuyo diseño se ha omitido no tiene una altura conocida, por lo que se le asigna un tamaño como si estuviera vacía. Con cientos de tarjetas omitidas, la altura total del documento resulta gravemente incorrecta, y la barra de desplazamiento refleja esa altura errónea. A medida que las tarjetas entran en la vista y adquieren su tamaño real, el documento crece y el cursor de desplazamiento se mueve.
contain-intrinsic-size indica el tamaño que se debe asumir cuando se omite un elemento. El 340px del ejemplo es una estimación para tarjetas que aún no se han renderizado. No se requiere precisión; una aproximación razonable evita que la barra de desplazamiento se mueva de forma visible.
Qué agrega la palabra clave auto
La palabra clave auto es fácil de pasar por alto, pero tiene una gran importancia. Con auto, una vez que se ha renderizado una tarjeta, el navegador registra su tamaño real. Si posteriormente la tarjeta sale del área de visualización y se vuelve a omitir, el navegador utiliza ese tamaño memorizado en lugar de la estimación que usted proporcionó. Sin auto, cada tarjeta vuelve a utilizar la estimación fija cada vez que sale de la vista al desplazarse.
Las tarjetas en la pantalla siempre se muestran a su altura real, sin importar las circunstancias. La diferencia aparece con contenido de altura variable: un nombre de producto largo que se extiende a una línea adicional, o un distintivo de oferta que añade una fila. Sin auto, esas tarjetas vuelven a la altura estimada cuando salen del área de visualización y la posición de desplazamiento cambia bruscamente. Utilice auto en todo momento.
Soporte de navegadores y mejora progresiva
El soporte ya no es exclusivo de Chrome. Chrome admite content-visibility desde la versión 85 (2020), Firefox desde la 125, y Safari desde la versión 18. En el momento de redactar este texto se encuentra clasificado como “Baseline Newly Available”, un estado alcanzado el 15 de septiembre de 2025, lo que significa que funciona en los tres principales motores de navegadores; consulte los datos de compatibilidad actuales si necesita soportar versiones antiguas.
Los navegadores que no comprenden esta propiedad simplemente la ignoran y muestran todo como siempre lo han hecho. Eso hace que se trate de una mejora progresiva sin inconvenientes para los clientes más antiguos.
Cómo se relaciona con Core Web Vitals y SEO
La motivación aquí es el rendimiento, pero las páginas de categoría son exactamente el tipo de URL que los equipos de búsqueda vigilan atentamente. Son públicas, apuntan a consultas valiosas, y Google mide su rendimiento para los usuarios reales.
El diseño y el proceso de dibujo influyen en dos de los tres Core Web Vitals que Google utiliza como señal de clasificación:
- Interaction to Next Paint (INP) se beneficia directamente. Omitir el procesamiento de tarjetas fuera de la pantalla libera el hilo principal, por lo que los toques y clics reciben una respuesta más rápido.
Muchas páginas que no son comerciales tienen esta estructura, desde documentación y feeds de noticias hasta archivos de publicaciones de blog, hilos de discusión extensos y artículos con varias secciones. Dondequiera que haya muchos elementos similares apilados verticalmente y la mayoría esté fuera de pantalla, y la página sea pública, este cambio afecta las métricas que mide Google.
Tenga cuidado al prometer mejoras en el posicionamiento. Observar un único sitio durante unas pocas semanas demuestra muy poco, y el posicionamiento depende de mucho más que una sola métrica. Lo que puede esperar razonablemente es un movimiento en la dirección correcta en los datos de campo, por ejemplo en PageSpeed Insights, una vez que se acumulen suficientes muestras de usuarios reales.
La ventaja no se limita a las páginas públicas. Los paneles de control, las tablas administrativas y las herramientas internas también sufren los mismos costos de renderizado de 600 filas. Detrás de un proceso de inicio de sesión no hay beneficio adicional en términos de búsqueda, pero la mejora en la experiencia del usuario es la misma, gracias al mismo CSS.
¿El omisión del renderizado oculta el contenido de los rastreadores?
Esta es la primera pregunta que hará un especialista en SEO cuidadoso, y es lógica teniendo en cuenta cuántos esquemas de carga diferida han hecho que el contenido sea invisible para los bots. La distinción clave es que content-visibility: auto es una optimización de renderizado, no un cambio en la visibilidad. El contenido existe en el HTML y en el DOM desde el momento en que se carga la página; solo el diseño y la pintura se posponen hasta que el elemento se acerca al área visible de la pantalla.
Googlebot no desplaza la página como lo haría una persona. En lugar de eso, utiliza una ventana de visualización extremadamente alta para renderizar y luego examina el DOM resultante. Las tarjetas que se omiten pero que están presentes forman parte de ese DOM, por lo que cada título y precio del producto se indexa al igual que cualquier otro contenido.
En contraste con esto está el patrón anterior de insertar contenido solo cuando se dispara un evento de desplazamiento. Los rastreadores no generan eventos de desplazamiento, por lo que dicho contenido realmente podría faltar en el índice. content-visibility no puede causar este problema, ya que nada se inserta posteriormente; todo está presente desde el principio.
Una advertencia no está relacionada con la propiedad en sí. Si la página genera su contenido con JavaScript del lado del cliente, surge un problema de SEO separado. Googlebot sí ejecuta JavaScript, pero su renderizado se hace de forma secuencial, lo que resulta más lento y menos fiable; además, muchos otros rastreadores manejan mal el JavaScript. Para contenido público, renderice el HTML en el servidor y añada content-visibility en CSS. Esa combinación permite obtener contenido rastreable y mejores indicadores de rendimiento.
En comparación con el desplazamiento infinito, la virtualización y los observadores personalizados
Tanto el desplazamiento infinito como las APIs paginadas y la virtualización abordan el mismo problema de páginas largas y lentas, por lo que vale la pena compararlos honestamente.
Carga al desplazarse y APIs paginadas
Cargar más elementos a medida que el usuario desplaza la página resuelve el problema a nivel de datos. Es la opción adecuada cuando el conjunto de datos es realmente enorme y nunca debería enviarse completo al navegador.
Tiene sus costos. Se requieren cambios en la API, estados de carga, listeners de desplazamiento y seguimiento del estado de lo que ya se ha recuperado. La experiencia de uso también cambia: la función de búsqueda dentro de la página no puede localizar elementos que aún no se han cargado, y llegar al final de la lista se vuelve tedioso. En una página pública de categorías existe además una carga adicional en términos de SEO, ya que los productos que solo aparecen al desplazarse no existen para los rastreadores; se necesitan URLs de respaldo paginadas y marcado adicional para mantenerlos indexables. Para 600 tarjetas que ya están en el HTML, esto implica una reescritura además de trabajo adicional de SEO para solucionar lo que en realidad es un problema de renderizado.
Bibliotecas de virtualización
La virtualización, con bibliotecas como react-window o TanStack Virtual, mantiene todo el conjunto de datos en memoria mientras descarta los nodos DOM de lo que está fuera del área visible. Funciona bien, y a escalas muy grandes es la mejor opción, como se explicará más abajo.
El precio se debe a una dependencia de JavaScript, a la reescritura de un componente y al manejo sumamente complicado de filas de altura variable. Dado que los elementos eliminados no están en absoluto en el DOM, find-in-page no puede verlos, los lectores de pantalla tienen dificultades con ellos y, en una página pública, los rastreadores los pasan por alto.
Un enfoque basado en IntersectionObserver desarrollado a mano
Renderizar los elementos por uno mismo cuando IntersectionObserver indica que son visibles implica reconstruir con JavaScript en el hilo principal lo que content-visibility: auto ya hace, además de reintroducir errores relacionados con el anclaje al desplazamiento que el navegador ya resolvió de forma nativa. Hay pocas razones para implementarlo hoy en día.
Elegir entre uno u otro
El verdadero argumento a favor de content-visibility no es que supere a estas técnicas, sino que es mucho más económico. Se trata de una propiedad CSS: sin JavaScript, sin cambios en la API, sin reescritura, y el contenido permanece en el DOM para búsquedas, tecnologías de asistencia y la función “encontrar en la página”.
El compromiso debe exponerse claramente. content-visibility ahorra trabajo de renderizado, no memoria. Cada nodo del DOM sigue existiendo. Con 600 tarjetas esto es insignificante. Pero con 50,000 o 100,000 elementos, el tamaño del DOM se convierte en un problema en sí mismo, y la virtualización cobra sentido debido a su complejidad. Identifique cuál de los dos problemas tiene, el costo de renderizado o el tamaño del DOM, antes de elegir la herramienta.
Dónde funciona y dónde rompe las cosas silenciosamente
No se trata de una propiedad que deba aplicarse en todas partes. Da mejores resultados donde la misma estructura se repite muchas veces, por ejemplo:
- tarjetas en un catálogo o lista
Cualquier elemento que forme parte de varios bloques similares apilados verticalmente, siendo la mayoría de ellos fuera de pantalla al cargar, es un buen candidato para probar.
Medir el contenido omitido da resultados incorrectos
Esta propiedad entra en conflicto con códigos que necesitan geometría precisa de un subárbol antes de que dicho subárbol se haya renderizado. Un ejemplo típico es leer la altura de un elemento interno de una fila que aún está fuera de pantalla.
const height = row
.querySelector('.details')
.getBoundingClientRect()
.height;
Medir el contenido interno de un elemento omitido con getBoundingClientRect() antes de que haya sido renderizado en absoluto produce valores cero o incorrectos. El propio contenedor del elemento indica el tamaño del marcador de posición a partir de contain-intrinsic-size, lo cual tampoco necesariamente coincide con la realidad. Este tipo de código es común en interfaces reales, por ejemplo cuando:
- anclas una herramienta de ayuda junto a un desencadenante
- determina los valores de inicio y fin para una animación
- decide dónde se abrirá un menú desplegable
- determina el tamaño de las filas en una lista virtual
- gestiona la lógica de posicionamiento adherido
- alinea un componente con otro
Si la geometría exacta es importante antes de que el contenido sea visible, realice pruebas exhaustivas antes de añadir esa propiedad.
Otros casos poco adecuados
- Encabezados adherentes, además de diseños cálculos que solo funcionan cuando todos los hijos tienen una geometría real al mismo tiempo.
- Cualquier elemento que se encuentre por encima del borde visible. Ese contenido debe renderizarse de inmediato sin importar nada, por lo que esta propiedad no aporta beneficio alguno y además implica un poco más de trabajo administrativo.
- Elementos cuyos efectos visuales se extienden más allá de su contenedor. Dado que esta propiedad aplica la restricción de pintura, el contenido que sobresale, como sombras grandes o ventanas emergentes posicionadas dentro de la tarjeta, puede ser recortado en el borde de esta.
Dos detalles menos conocidos
El valor oculto
content-visibility: hidden omite la renderización de forma similar a display: none, pero el navegador mantiene en caché el estado de renderización del elemento. Mostrarlo nuevamente es considerablemente más económico que hacer visible un elemento con display: none, ya que no es necesario volver a realizar todo el proceso desde cero. Esto lo hace útil para pestañas, menús fuera de pantalla y desplazadores virtuales. A diferencia de auto, el contenido bajo hidden no es accesible mediante la función de búsqueda en la página mientras está oculto.
Reaccionar a los cambios de estado de omisión
Cada vez que un elemento que utiliza content-visibility: auto pasa de estar omitido a renderizado, el navegador envía un evento contentvisibilityautostatechange. Escuchar este evento permite suspender scripts costosos, como los de dibujo en canvas, para aquel contenido que el navegador de todos modos no está renderizando.
Verificar el efecto en sus propias páginas
Un experimento rápido deja la diferencia evidente. Crea una página de prueba con alrededor de 1,000 tarjetas y un botón que añada o elimine content-visibility: auto. Abre DevTools, ve al panel de Rendimiento, graba una nueva carga con el botón desactivado y luego grábala nuevamente con él activado, y compara los bloques de Layout de color púrpura en las dos grabaciones.
Luego aplica la misma verificación a tu página de producción más larga. Captura una grabación mientras se carga. Cuando la parte correspondiente al Layout domina y la interactividad llega tarde, añadir content-visibility: auto a los elementos repetidos suele ser la mejor solución económica disponible: una sola propiedad, sin reescribir código ni migrar a otro framework.
Puntos clave
- Los navegadores gestionan el estilo y el diseño de todo el documento;
content-visibility: autoles permite posponer ese trabajo para los elementos que se encuentran lejos del área de visualización sin eliminar nada del DOM. - Siempre úselo junto con
contain-intrinsic-size: auto <estimate>en el mismo cambio, de lo contrario la barra de desplazamiento cambiará bruscamente de posición. - El contenido sigue siendo indexable, buscable y accesible, lo que lo diferencia de las técnicas de carga al desplazarse y de la virtualización.
- Ahorra tiempo de renderizado, no memoria; una vez que el propio DOM se vuelve demasiado grande, la virtualización es la solución adecuada.
- Evítelo en las secciones superiores de la página, en elementos cuya geometría se mide antes de su visualización y en componentes que dibujan fuera de sus propios límites.
Lecturas relacionadas
- De la hoja de estilos a la pantalla: dónde encaja CSS en el proceso del navegador — Sigue a CSS desde su descarga hasta que se convierte en píxeles: cómo se construyen el DOM, CSSOM y el árbol de renderizado, dónde tiene lugar la cascada de estilos y qué fuentes de estilo compiten por cada elemento.
- De 66% a 185px: cómo resuelven los navegadores los valores CSS antes del diseño — Sigue a un valor CSS a través de las etapas declarada, en cascada, especificada, calculada, utilizada y real, y comprende por qué las unidades relativas y el truco de conversión a rem se comportan de la manera en que lo hacen.