loading="lazy"en un<iframe>retrasa la carga del documento incrustado hasta que el marco se acerca al viewport. Es la forma más barata de recortar peso inicial en embeds de YouTube, Maps o redes sociales.- No lo apliques a un iframe visible en el primer pantallazo, y menos si es el elemento LCP. El valor por defecto,
eager, es el correcto en ese caso. - Pon siempre
widthyheight(oaspect-ratioen CSS): Chrome reserva espacio con un marcador, pero la documentación recomienda declararlos igualmente para evitar saltos de diseño. - Google documenta el lazy loading nativo como método compatible y avisa de que Google Search no interactúa con la página: no hace scroll ni clics. El contenido debe cargarse cuando entra en el viewport.
- Una facade (miniatura que carga el vídeo al hacer clic) ahorra más que el lazy nativo, pero depende de una interacción: si el contenido del vídeo es indexable, no debe vivir solo detrás de ese clic.
- Se comprueba con DevTools (pestaña Network y un scroll) y con el HTML renderizado de Inspección de URL, no con una auditoría automática: la de imágenes fuera de pantalla de Lighthouse ya no existe.
Un solo iframe de YouTube puede añadir cientos de kilobytes de JavaScript a una página que solo quería enseñar un vídeo más abajo. Según web.dev, diferir ese embed ahorra en torno a 500 KB en la carga inicial; en el de Spotify, 514 KB. Con un atributo de una línea. Y aun así es fácil aplicarlo donde no toca, o creer que ya está resuelto cuando no lo está.
Aquí cubro cómo funciona loading="lazy" en iframes, qué navegadores lo soportan, qué no diferir nunca, cómo reservar el espacio para no provocar CLS, cuándo pasar a una facade, qué ve Googlebot y cómo medirlo. Termino con lo que hay en mi web, que en este caso es un dato poco glamuroso: hoy no uso ningún iframe.

Qué hace loading="lazy" en un iframe
Un iframe incrusta otro documento HTML dentro de tu página. Ese documento tiene su propia petición, sus propios recursos y su propio JavaScript, todo ello aunque el usuario nunca llegue a verlo. El atributo loading le dice al navegador cuándo empezar esa carga. MDN documenta dos valores:
eager: carga inmediata al procesar la página. Es el valor por defecto, así que un iframe sin atributo se comporta así.lazy: difiere la carga hasta que el iframe se acerca al viewport, a una distancia que decide el navegador.
web.dev lo describe con precisión: lazy difiere la carga del HTML del iframe y de sus subrecursos hasta que está dentro de una distancia predefinida respecto al viewport. No es solo ahorrar un HTML: es no arrancar el JavaScript, las fuentes y las peticiones de terceros que arrastra el embed.
<iframe
src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
title="Título descriptivo del vídeo"
width="560" height="315"
loading="lazy"
allowfullscreen></iframe>
Hay un matiz que casi nadie cuenta: según MDN, el diferido solo se aplica si JavaScript está habilitado. Es una medida contra el rastreo del usuario: sin scripts, una web podría deducir hasta dónde ha hecho scroll una persona contando qué iframes se solicitan y cuándo.
Soporte de navegadores
El soporte es amplio, con una excepción histórica en Firefox. Estos son los datos que he verificado:
| Navegador | Versión mínima | Fuente |
|---|---|---|
| Chrome | 77 | web.dev |
| Edge | 79 | web.dev |
| Firefox | 121 | web.dev; caniuse marca soporte parcial de la 75 a la 120 |
| Safari | 16.4 | web.dev |
Caniuse da un 96,68 % de soporte global para el atributo loading (la ficha agrupa imágenes e iframes, y no desglosa un porcentaje solo para iframes). MDN lo clasifica como Baseline «widely available».
La degradación es limpia: un navegador que no entiende loading lo ignora y carga el iframe de inmediato. Nada se rompe; simplemente no ahorras. Por eso no necesitas un polyfill salvo que quieras diferir también en navegadores antiguos, y entonces web.dev sugiere la librería lazysizes.
A qué distancia del viewport empieza a cargar
Aquí conviene ser prudente. MDN dice que la distancia la calcula el navegador y web.dev, que varía según el navegador. No hay un valor garantizado por estándar. Chrome sí publicó cifras, pero para imágenes: en conexiones rápidas (4G) bajó el umbral de 3000 px a 1250 px, y en lentas (3G o inferior) de 4000 px a 2500 px, en julio de 2020. La página no especifica umbrales propios para iframes, así que no los extrapolo.
La consecuencia práctica es que un iframe a un par de pantallas de distancia puede empezar a cargar antes de que el usuario lo vea, y eso está bien: el objetivo es que esté listo al llegar. Lo que no puedes hacer es depender de un número concreto para decidir el diseño. Si necesitas un control exacto de cuándo cargar, la herramienta es un IntersectionObserver con tu propio rootMargin, que veremos más abajo.
Qué no diferir nunca
La regla de Google para el lazy loading es clara: no lo añadas a contenido que probablemente se vea inmediatamente al abrir la página. Un iframe en esa posición tarda más en mostrarse, porque el navegador espera a conocer el layout antes de pedirlo.

- Iframes visibles en el primer pantallazo, en móvil y en escritorio. Mira ambos: un embed que en escritorio queda al lado del título puede quedar debajo en móvil, y al revés.
- Un iframe que sea el elemento LCP. web.dev dice «Never lazy-load your LCP image» para imágenes, y el motivo es transferible: el diferido retrasa el inicio de la petición hasta que el layout confirma que el elemento está en el viewport. La guía de web.dev no da una regla específica de LCP para iframes, pero el mecanismo es el mismo. Para un LCP bueno, el objetivo es 2,5 s o menos en al menos el 75 % de las visitas.
- Iframes que cumplen una función inmediata: un widget de reservas en la cabecera, un formulario de pago, un chat que el usuario espera. Si el usuario los busca nada más llegar, que carguen ya.
Mi criterio es mecánico: abro la página en móvil, miro qué iframes caen dentro de la primera pantalla y a esos les dejo eager (o no pongo el atributo). Al resto, lazy. Si un iframe es LCP, me planteo si debe ser un iframe: casi siempre es mejor una imagen que enlace al contenido. Tienes el marco general en mi guía de Core Web Vitals.
width y height para evitar CLS
Un iframe sin dimensiones mide, por defecto, 300 × 150 px (MDN). Cuando carga y el contenido pide más altura, empuja todo lo que tiene debajo: eso es layout shift. El umbral de un CLS bueno es 0,1 o menos, medido en el percentil 75 de las cargas.
Con lazy loading hay un detalle adicional. Según web.dev, Chrome reserva espacio y muestra un marcador mientras el iframe se descarga, pero recomienda igualmente usar los atributos width y height y estilos CSS. Yo lo resuelvo con ambos: atributos para que el navegador conozca la proporción antes de aplicar CSS, y aspect-ratio para que sea fluido.

iframe.embed {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
border: 0;
}
Dos avisos. El primero: una regla CSS de ancho como width: 100% tiene prioridad sobre el atributo HTML, así que el atributo sirve de proporción base y el CSS manda en pantalla. El segundo: la API de layout shift no reporta los desplazamientos que ocurren dentro del iframe, aunque sí cuenta los que provoca en tu página. Si un embed de terceros se redimensiona solo (anuncios, widgets), tu contenedor debe tener una altura fija o mínima.
Nativo, IntersectionObserver o facade
Hay tres maneras de diferir un embed y no resuelven lo mismo.

| Método | Cuándo carga | Coste | Úsalo si… |
|---|---|---|---|
| loading="lazy" | Cerca del viewport, distancia del navegador | Una línea de HTML | El embed está debajo del pliegue y no necesitas control fino |
| IntersectionObserver | Cuando tú decides (rootMargin) | JavaScript propio | Quieres otra distancia o cargar con lógica añadida |
| Facade | Al hacer clic (o al pasar el ratón) | Miniatura + JS ligero | El embed es pesado y la mayoría de visitas no lo reproduce |
Lazy nativo
Es mi opción por defecto: sin JavaScript, sin dependencias, con Baseline de soporte. El ejemplo de Google Maps Embed API usa el atributo en su propia documentación:
<iframe
width="600" height="450"
style="border:0"
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin"
src="https://www.google.com/maps/embed/v1/place?key=API_KEY&q=Space+Needle,Seattle+WA"></iframe>
IntersectionObserver
Útil cuando quieres decidir la distancia o cuando el src se asigna por script. Google lo cita como método compatible para el contenido diferido, con polyfill disponible. Este patrón mantiene la URL real en data-src, así que añade un noscript con el iframe normal para quien no ejecute JavaScript:
<iframe data-src="https://www.google.com/maps/embed?pb=..."
width="600" height="450" title="Mapa de la oficina"></iframe>
<noscript><iframe src="https://www.google.com/maps/embed?pb=..."
width="600" height="450" title="Mapa de la oficina"></iframe></noscript>
<script>
const io = new IntersectionObserver((entries, obs) => {
entries.forEach(e => {
if (!e.isIntersecting) return;
e.target.src = e.target.dataset.src;
obs.unobserve(e.target);
});
}, { rootMargin: '600px 0px' });
document.querySelectorAll('iframe[data-src]').forEach(f => io.observe(f));
</script>
El rootMargin de 600 px es un valor que he puesto yo como ejemplo, no una recomendación oficial: ajústalo midiendo. Ten en cuenta que aquí el iframe no tiene src hasta que el observer actúa; es más frágil que el atributo nativo, y por eso solo lo uso cuando el nativo no me sirve.
Facade (click-to-load) para YouTube
Una facade sustituye el iframe por una miniatura con un botón de reproducir y solo crea el iframe al hacer clic. web.dev recomienda el componente lite-youtube-embed de Paul Irish para YouTube; su README lo presenta como «approximately 224× faster» que un iframe normal (la cifra es suya y no detalla cómo se mide) y usa youtube-nocookie.com.
<link rel="stylesheet" href="lite-yt-embed.css">
<script src="lite-yt-embed.js" defer></script>
<lite-youtube videoid="VIDEO_ID"
playlabel="Reproducir: título del vídeo"></lite-youtube>
Si prefieres no depender de una librería, esta versión mínima es mía y la propongo como punto de partida, no como código probado en producción en esta web. Incluye un enlace real al vídeo, de modo que sin JavaScript el usuario sigue pudiendo verlo:
<div class="yt-facade" data-id="VIDEO_ID" style="aspect-ratio:16/9">
<a href="https://www.youtube.com/watch?v=VIDEO_ID">Ver el vídeo en YouTube</a>
</div>
<script>
document.querySelectorAll('.yt-facade').forEach(el => {
el.addEventListener('click', ev => {
ev.preventDefault();
const f = document.createElement('iframe');
f.src = 'https://www.youtube-nocookie.com/embed/' + el.dataset.id + '?autoplay=1';
f.width = 560; f.height = 315;
f.title = 'Vídeo de YouTube';
f.allow = 'autoplay; encrypted-media; fullscreen';
el.replaceChildren(f);
}, { once: true });
});
</script>
La facade es lo que más ahorra, porque no pide nada de YouTube hasta el clic. El coste es que añade un paso para el usuario, y que lo que hay detrás del clic no existe para un rastreador que no interactúa. Esto último lo desarrollo en la siguiente sección.
Cómo lo ve Googlebot
La guía de Google sobre lazy loading parte de un aviso: si no se implementa bien, la técnica puede ocultar contenido a Google sin que te des cuenta. Lo que pide es que la implementación cargue todo el contenido relevante cuando es visible en el viewport. Y añade que los métodos que recomienda no dependen de acciones del usuario como hacer scroll o clic, porque Google Search no interactúa con la página. Es la misma lógica que rige para cualquier contenido que dependa de JavaScript, y la desarrollo en mi guía de JavaScript SEO.

Traducido a iframes:
loading="lazy"nativo: la página de Google lo lista entre los métodos compatibles, con imágenes e iframes. Es la opción más segura.- IntersectionObserver: también compatible, siempre que cargue el contenido al entrar en el viewport.
- Facade o carga por clic: depende de una interacción que Google Search no realiza. Si el vídeo o el mapa aportan contenido que quieres que se indexe, ese contenido no puede vivir solo detrás del clic. Pon el texto relevante (transcripción, dirección, resumen) en el HTML de tu página.
Lo que esa página no dice, y por tanto no afirmo: no detalla el tamaño del viewport de renderizado ni a qué distancia se activa un lazy nativo dentro de Googlebot. Tampoco menciona noscript ni el atributo loading por su nombre; solo habla de «browser built-in lazy-loading». Para comprobarlo, Google propone usar Inspección de URL en Search Console y revisar el HTML renderizado.
Y hay una cuestión más amplia: el contenido de un iframe pertenece a otra URL, no a tu página. Qué se indexa de él y a quién se atribuye lo trato en mi guía de iframes y SEO; aquí me limito a cómo cargarlos.
Errores típicos

- Lazy en el iframe que es LCP. Lo normal cuando un plugin o un tema aplica
loading="lazy"a todos los iframes sin distinguir cuál está en la primera pantalla. El LCP se retrasa porque la petición no sale hasta que el layout confirma que el marco es visible. data-srcsinnoscriptni alternativa. Si elsrcreal solo se asigna por script, el HTML inicial no contiene la URL del embed. Con un observer funciona, pero quien no ejecute el script no ve nada; añade elnoscripto un enlace.- Altura cero. Un contenedor sin altura definida, o un iframe con
height="0"pensado para «cargar en segundo plano». Un marco sin altura real no es visible para el usuario, y cuando por fin se le da espacio el contenido aparece de golpe y desplaza el diseño. No lo he probado en todos los navegadores, pero no es un patrón que recomiende. - Un plugin que lo aplica a todo. Tanto los iframes de analítica como los visibles reciben el atributo. Revisa el HTML final, no los ajustes del plugin.
- Contenido solo tras un clic. Una facade o una pestaña que no carga hasta interactuar esconde ese contenido a quien no interactúa, Google Search incluido.
- Probar solo en escritorio. El pliegue cambia en móvil, y con la indexación mobile-first esa es la versión que cuenta (tienes el contexto en mi guía de SEO móvil): lo que en escritorio queda abajo puede estar en la primera pantalla de un teléfono.
El segundo error me recuerda a la diferencia entre lo que sirve el servidor y lo que construye el navegador; si trabajas con frameworks, revisa también CSR, SSR, SSG e ISR.
Cómo medirlo

1. Inventario. Pega esto en la consola de DevTools para listar cada iframe con su atributo y sus dimensiones:
[...document.querySelectorAll('iframe')].map(f => ({
src: f.src || f.dataset.src,
loading: f.loading,
w: f.getAttribute('width'),
h: f.getAttribute('height'),
top: Math.round(f.getBoundingClientRect().top + scrollY)
}))
El valor top te dice a cuántos píxeles del inicio está cada uno. Todo lo que quede dentro de la altura de la ventana en móvil debería ser eager.
2. Pestaña Network. Recarga con la caché desactivada, sin hacer scroll, y filtra por «Doc»: los iframes que aparezcan son los que se cargan de entrada. Haz scroll despacio y observa cuándo aparece cada uno. Si un iframe que está lejos ya se descargó, no tiene loading="lazy" o el navegador lo trata como cercano.
3. Lighthouse y PageSpeed Insights. Aquí un aviso útil: la auditoría «offscreen images» de Lighthouse, la que sugería diferir imágenes fuera de pantalla, figura como eliminada desde Lighthouse 13 en la documentación de Chrome, y esa página no dice que cubriera iframes. No esperes que te avise de un embed pesado: busca el peso de terceros en la traza y vigila el LCP y el CLS.
4. HTML renderizado. En Inspección de URL de Search Console, abre la prueba en vivo y revisa el HTML renderizado: el src del iframe debería aparecer si el marco carga en el viewport. Es el equivalente a lo que Google sugiere comprobar para imágenes y vídeos diferidos.
Haz la comprobación dos veces: con red rápida y con la limitación «Slow 4G» de DevTools. Un iframe que parece cargar a tiempo con tu fibra puede llegar tarde en un móvil de gama media.
Cómo lo trato en mi web
He revisado el código del sitio y la conclusión es sencilla: hoy no uso ningún iframe. No hay etiquetas <iframe> en las plantillas, en los includes, en los scripts ni en los artículos publicados. No hay mapa incrustado en contacto ni vídeos de YouTube embebidos, así que en iframes no hay nada que diferir.

Lo que sí hay son imágenes con loading="lazy", que es el mismo mecanismo (el criterio completo está en mi guía de optimización de imágenes para SEO):
- Las imágenes secundarias de la portada (logos de clientes, flechas decorativas, retrato editorial) y las tarjetas del blog van con
loading="lazy"ydecoding="async". - La imagen de portada de cada artículo no lleva
loading="lazy": llevafetchpriority="high", porque es candidata a LCP. En las páginas de ciudad, la imagen principal usaloading="eager"yfetchpriority="high". - El avatar del autor y la tarjeta de contacto del final de cada artículo van con
loading="lazy".
Un dato relacionado para no confundir: mis cabeceras incluyen X-Frame-Options: SAMEORIGIN. Eso controla si otras webs pueden incrustar mis páginas, no si yo incrusto las suyas, y no tiene efecto sobre el lazy loading.
Si mañana añadiera un mapa en la página de contacto, haría lo siguiente: iframe nativo con loading="lazy", width, height y aspect-ratio, un title descriptivo, la dirección escrita en el HTML (para que el dato no dependa del mapa) y, si el mapa quedara dentro de la primera pantalla en móvil, sin el atributo. Para un vídeo, probaría primero el nativo y pasaría a una facade solo si la medición mostrara que el embed pesa demasiado, con la transcripción en la página.
Checklist de revisión
- Lista los iframes con el snippet de consola y anota su posición en móvil.
- Deja sin atributo (o con
eager) los que caen en la primera pantalla; añadeloading="lazy"al resto. - Comprueba que todos tienen
widthyheightoaspect-ratio, y untitle. - Si usas
data-src, añadenoscripto un enlace con la URL real. - Pasa a facade solo los embeds pesados que casi nadie reproduce, y pon en el HTML el texto que quieras indexar.
- Verifica en DevTools (Network con scroll) y en el HTML renderizado de Inspección de URL.
Preguntas frecuentes
¿Se puede usar loading="lazy" en iframes?
Sí. MDN lo documenta para el elemento iframe con los valores eager y lazy, y web.dev da soporte desde Chrome 77, Edge 79, Firefox 121 y Safari 16.4. En un navegador que no lo entienda, el iframe carga de inmediato.
¿A qué distancia del viewport empieza a cargar un iframe lazy?
La decide el navegador y varía entre ellos. Chrome publicó umbrales para imágenes (1250 px en 4G y 2500 px en conexiones lentas), pero esa página no da cifras específicas para iframes.
¿Hay que poner lazy loading a todos los iframes?
No. Los que se ven en el primer pantallazo deben cargar de inmediato, y con más motivo si son el elemento LCP. El lazy está pensado para los que quedan por debajo del pliegue.
¿Googlebot carga los iframes con lazy loading?
Google incluye el lazy loading nativo para imágenes e iframes entre los métodos compatibles, y pide que el contenido se cargue al entrar en el viewport, porque Google Search no hace scroll ni clics. No documenta más detalles, como la distancia exacta de activación. Compruébalo en el HTML renderizado de Inspección de URL.
¿Es mejor una facade que loading="lazy" para YouTube?
Ahorra más, porque no carga nada hasta el clic, pero añade un paso al usuario y su contenido depende de una interacción. Prueba primero el lazy nativo y usa facade si la medición lo justifica.
¿Un iframe con lazy loading puede causar CLS?
Si no tiene dimensiones, sí: mide 300 × 150 por defecto y salta al cargar. Chrome reserva espacio con un marcador, pero se recomienda declarar width y height o usar aspect-ratio.
¿Lighthouse me avisa de iframes fuera de pantalla?
No cuento con ello. La auditoría de imágenes fuera de pantalla figura como eliminada desde Lighthouse 13 y su documentación no menciona iframes. Mide con la pestaña Network y los datos de LCP y CLS.
Referencias y fuentes
Documentación consultada para este artículo.
- Fix lazy-loaded content Google Search Central
- <iframe>: The Inline Frame element MDN Web Docs
- Lazy-loading iframes web.dev
- Lazy loading images and <iframe> elements web.dev Learn Performance
- Browser-level image lazy loading for the web web.dev
- Optimize Largest Contentful Paint web.dev
- Cumulative Layout Shift (CLS) web.dev
- Lazy loading via attribute for images & iframes Can I use
- lite-youtube-embed GitHub · Paul Irish
- Defer offscreen images Chrome for Developers · Lighthouse
- Maps Embed API: primeros pasos Google Maps Platform




