Skip to content

SVGs en la web: comparativa de rendimiento según cómo los cargas

Published:

Los SVGs son ubicuos en la web: iconos, ilustraciones, logos, gráficas. Pero pocas veces nos paramos a pensar que la forma en que los cargamos tiene un impacto real en rendimiento. No es lo mismo un SVG inline en el HTML que uno referenciado desde un <img>: cambia la caché, el renderizado, las peticiones de red y hasta cómo el navegador los prioriza.

En este artículo analizo las principales técnicas de carga de SVG desde el punto de vista del rendimiento, con sus ventajas, sus limitaciones y los anti-patterns más habituales que encuentro en auditorías.

Table of Contents

Open Table of Contents

Las formas de cargar un SVG

Hay cinco formas principales de incluir un SVG en una página web, y cada una tiene un comportamiento diferente:

MétodoCachéPetición de redAcceso CSS/JSReusable
Inline <svg>❌ No❌ No✅ Sí❌ No
<img src="icon.svg">✅ Sí✅ Sí❌ No✅ Sí
<object data="icon.svg">✅ Sí✅ SíLimitado✅ Sí
CSS background-image✅ Sí✅ Sí❌ No✅ Sí
SVG sprite + <use>✅ Parcial✅ Sí✅ Sí✅ Sí

SVG inline: máximo control, sin caché

Incrustar el SVG directamente en el HTML es la técnica que más control ofrece: acceso total desde CSS y JavaScript, sin peticiones adicionales, compatible con animaciones y con currentColor.

<!-- ✅ Ventajas: acceso CSS/JS, sin petición de red -->
<svg
  xmlns="http://www.w3.org/2000/svg"
  width="24"
  height="24"
  viewBox="0 0 24 24"
>
  <path d="M12 2L2 7l10 5 10-5-10-5z" />
</svg>

El problema: cada vez que la página se carga, el SVG se parsea de nuevo. No hay caché del SVG en sí: está embebido en el HTML. Si el mismo icono aparece en 50 páginas distintas, se descarga y parsea 50 veces.

<!-- ❌ Repetir el mismo SVG en cada página tiene coste de parseo -->
<!-- página-1.html -->
<svg>...</svg>

<!-- página-2.html -->
<svg>...</svg>
<!-- el navegador no lo cachea entre páginas -->

Cuándo tiene sentido: SVGs únicos por página (logos, ilustraciones hero), cuando necesitamos animarlos con CSS o JS, o cuando el SVG es el LCP candidate y quieres evitar la petición de red.

<img>: simple, cacheado, pero opaco

Referenciar un SVG desde <img> es la forma más limpia para iconos y gráficas que no necesitan interacción:

<!-- ✅ Cacheado, sin bloquear el parser -->
<img
  src="/icons/arrow.svg"
  width="24"
  height="24"
  alt="Siguiente"
  loading="lazy"
/>

El navegador lo trata como cualquier imagen: lo cachea, lo prioriza según loading y fetchpriority, y lo puede compartir entre páginas. El SVG no tiene acceso a los estilos del documento: currentColor no funciona, y no es posible manipularlo con JavaScript.

<!-- ❌ currentColor no hereda el color del documento -->
<img src="/icons/icon.svg" style="color: red" />
<!-- El fill del SVG no cambiará -->

<object>: documento embebido

<object> carga el SVG como un documento independiente con su propio contexto. Tiene caché, pero el acceso desde el documento padre es limitado y requiere scripting adicional. En la práctica, es la opción menos usada y con más fricción.

<object data="/graphic.svg" type="image/svg+xml" width="200" height="200">
  <!-- Fallback para navegadores sin soporte -->
  <img src="/graphic.png" alt="Gráfico" />
</object>

Para la mayoría de casos, <img> o inline son mejores alternativas.

CSS background-image: SVGs decorativos

Para SVGs puramente decorativos (separadores, patrones, fondos) que no necesitan texto alternativo ni interacción, background-image es válido:

/* ✅ Para elementos decorativos */
.divider {
  background-image: url("/decorative/wave.svg");
  background-size: cover;
  height: 80px;
}

Sin acceso CSS ni JS al contenido interno del SVG, y sin semántica para lectores de pantalla. Bien para decoración, mal para iconos con significado.

SVG sprite + <use>: el mejor de ambos mundos

El sprite SVG es la técnica más eficiente para iconos reutilizables: un único archivo con todos los símbolos, referenciado desde <use>. Una sola petición, cacheada, compartida entre páginas.

<!-- sprite.svg (cargado una vez) -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  <symbol id="icon-arrow" viewBox="0 0 24 24">
    <path fill="currentColor" d="M5 12h14M12 5l7 7-7 7" />
  </symbol>
  <symbol id="icon-close" viewBox="0 0 24 24">
    <path fill="currentColor" d="M18 6L6 18M6 6l12 12" />
  </symbol>
</svg>

<!-- Uso en cualquier parte del HTML -->
<svg width="24" height="24" aria-hidden="true">
  <use href="/sprite.svg#icon-arrow" />
</svg>

El sprite se cachea en el navegador. Cada <use> solo referencia un símbolo, no descarga el archivo de nuevo. Además, mantiene acceso a currentColor para colorear con CSS.

/* ✅ currentColor funciona con <use> si el SVG usa fill="currentColor" */
.icon {
  color: var(--color-primary); /* se hereda como currentColor */
}

Limitación a tener en cuenta: <use> con referencia externa (href apuntando a otro dominio) puede tener restricciones de CORS.

Anti-pattern: imágenes de bits incrustadas en SVG

Este es uno de los anti-patterns que más impacto tiene en rendimiento y que encuentro con frecuencia en auditorías. Un SVG puede contener imágenes rasterizadas (WebP, JPEG, PNG) codificadas en base64 dentro del propio archivo:

<!-- ❌ Anti-pattern: imagen rasterizada embebida en SVG -->
<svg xmlns="http://www.w3.org/2000/svg">
  <image href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." />
</svg>

¿Por qué es un problema?

  1. Tamaño disparado: base64 añade un 33% de overhead al peso del archivo
  2. Sin optimización: el navegador no puede aplicar compresión adaptativa ni negociación de formato (AVIF, WebP)
  3. Sin caché granular: la imagen rasterizada no se cachea por separado; si cambia, se invalida todo el SVG
  4. Bloquea el renderizado: el SVG no se muestra hasta que se descarga y decodifica la imagen embebida
<!-- ✅ Referencia externa: la imagen se cachea y optimiza por separado -->
<svg xmlns="http://www.w3.org/2000/svg">
  <image href="/photos/team-photo.jpg" width="800" height="600" />
</svg>

Los exportadores de herramientas de diseño como Figma o Illustrator a veces generan este patrón cuando el diseño incluye imágenes rasterizadas. Revísalo siempre antes de publicar.

¿Cómo detectarlo?

En DevTools puedes ver el tamaño real de los SVGs en el panel Network. Un SVG de más de 50KB suele ser sospechoso. Para analizarlo directamente en el navegador, el snippet SVG Embedded Bitmap Analysis de WebPerf Snippets recorre todos los SVGs de la página e identifica los que contienen imágenes rasterizadas embebidas. También puedes buscarlo en los archivos del proyecto:

# Busca SVGs con imágenes embebidas en base64
grep -rl "data:image" ./public --include="*.svg"

Comparativa de rendimiento: cuándo usar cada método

Conclusión

La elección del método de carga de SVG no es cosmética: afecta directamente a la caché, al número de peticiones, al parseo y al rendimiento percibido. El sprite con <use> es la opción más eficiente para sistemas de iconos. Para ilustraciones únicas con animación, el inline tiene sentido. Y siempre, siempre, evita incrustar imágenes de bits en SVGs.


Previous Post
WebAssembly: el estándar olvidado del W3C
Next Post
Guía práctica del elemento <img>: de lo básico a LCP