Saltar al contenido
ImageZap

Placeholders de imagen: LQIP, BlurHash, ThumbHash y color dominante

Un buen placeholder hace que tu web parezca más rápida sin cambiar un solo byte de las imágenes. Te explicamos qué técnicas existen, cuánto pesan y cómo implementarlas bien.

Por 4 min de lectura

El problema: huecos vacíos y contenido que salta

Cuando un navegador pinta una página, el texto aparece casi al instante, pero cada imagen necesita su propia descarga. Mientras tanto, el usuario ve uno de estos dos escenarios:

  1. Un hueco vacío (blanco o gris) que de repente se llena con la foto. Funciona, pero se percibe brusco y "lento".
  2. Nada, y cuando la imagen llega, empuja hacia abajo todo el contenido que había debajo. Es el peor caso: además de feo, penaliza el CLS (Cumulative Layout Shift), una de las métricas de Core Web Vitals.

El segundo escenario se soluciona siempre declarando width y height (o aspect-ratio en CSS), y es lo primero que debes hacer. El primero se mejora con un placeholder: rellenar el hueco reservado con una aproximación de la imagen que ocupe muy poco.

Las técnicas, de más ligera a más fiel

1. Color dominante (7 bytes)

Un solo color extraído de la imagen y aplicado como fondo del contenedor:

<img src="/foto.avif" width="1200" height="800" alt="…" style="background:#4a6b8a">

Es lo que hacen Pinterest, Google Imágenes o Unsplash en sus cuadrículas. No necesita JavaScript, no añade peticiones y se puede generar en la base de datos al subir la imagen. Para listados con muchas miniaturas es difícil de superar.

2. Degradado CSS (≈60 bytes)

Dos o más colores muestreados por zonas y combinados con linear-gradient. Transmite mejor la composición ("cielo arriba, arena abajo") que un color plano y sigue siendo CSS puro.

3. LQIP en base64 (100-300 bytes)

Low Quality Image Placeholder: la propia imagen reducida a 10-20 px, codificada como data URI y ampliada con un desenfoque CSS. Es la técnica de Medium y la que usa Next.js con placeholder="blur". Se ve claramente como "la misma foto desenfocada", lo que da una transición muy natural.

<img src="/foto.avif" width="1200" height="800" alt="…"
  style="background: url('data:image/webp;base64,UklGR…') center/cover">

4. BlurHash (≈20-30 caracteres)

Desarrollado por Wolt, codifica la imagen como unos pocos componentes de frecuencia (una transformada discreta del coseno muy reducida) en una cadena de texto compacta. Necesita JavaScript para decodificarse en un canvas, pero a cambio es lo bastante corto para guardarlo en cualquier columna de base de datos y viajar en una API sin notarse. Tiene implementaciones para web, iOS, Android, Flutter, Python, Go…

5. ThumbHash (≈30-45 caracteres)

Creado por Evan Wallace en 2023 como evolución de BlurHash. Con un tamaño similar consigue colores más fieles, guarda la relación de aspecto y admite transparencia, lo que lo hace ideal para productos recortados o logotipos.

Comparativa rápida

TécnicaTamañoNecesita JSTransparenciaFidelidad
Color dominante7 BNoNoBaja
Degradado CSS~60 BNoNoBaja-media
LQIP base64100-300 BNoSí (PNG/WebP)Alta
BlurHash~28 BSíNoMedia
ThumbHash~35 BSí (o data URL precalculada)SíMedia-alta

Implementación en Next.js

Con imágenes importadas estáticamente, Next.js calcula el LQIP durante la build y basta con una propiedad:

import Image from "next/image";
import hero from "@/public/hero.jpg";

<Image src={hero} alt="…" placeholder="blur" priority />

Con imágenes remotas (CMS, S3, Cloudinary) Next.js no puede descargarlas en la build, así que tienes que proporcionar tú el blurDataURL. Lo correcto es generarlo una vez, cuando se sube la imagen, y guardarlo junto a la URL:

<Image
  src={post.image.url}
  width={post.image.width}
  height={post.image.height}
  alt={post.image.alt}
  placeholder="blur"
  blurDataURL={post.image.blurDataURL}
/>

Si no tienes un proceso automático, el generador de placeholders te da el blurDataURL de un lote de imágenes en JSON listo para importar.

Implementación con BlurHash en React

import { Blurhash } from "react-blurhash";
import { useState } from "react";

function Foto({ src, hash, width, height, alt }) {
  const [loaded, setLoaded] = useState(false);
  return (
    <div style={{ position: "relative", aspectRatio: `${width} / ${height}` }}>
      {!loaded && <Blurhash hash={hash} width="100%" height="100%" style={{ position: "absolute", inset: 0 }} />}
      <img src={src} alt={alt} width={width} height={height} loading="lazy"
        onLoad={() => setLoaded(true)} style={{ width: "100%", height: "auto" }} />
    </div>
  );
}

Fíjate en que el contenedor tiene aspect-ratio: BlurHash no conoce la proporción de la imagen y, sin ella, el placeholder no reservaría el espacio correcto.

Errores que empeoran el rendimiento

  • Placeholders demasiado grandes. Un LQIP de 64 px puede superar 2 KB. Multiplicado por 40 miniaturas son 80 KB de HTML que el navegador debe descargar y analizar antes de pintar. Quédate en 16-20 px.
  • Decodificar BlurHash en el hilo principal para cientos de imágenes. Cada decodificación es barata, pero cientos a la vez bloquean la interacción (INP). Decodifica a 32 × 32 px y escala con CSS, nunca al tamaño final.
  • Retrasar la imagen LCP. Un placeholder no sustituye cargar pronto la imagen principal. La imagen LCP debe llevar fetchpriority="high" y nunca loading="lazy"; el placeholder es un complemento, no una excusa.
  • Animaciones de "fade-in" largas. Una transición de 1 segundo hace que la imagen tarde 1 segundo más en verse nítida. 200-300 ms son suficientes.
  • Olvidar el modo oscuro. Un placeholder gris claro destaca mucho en un tema oscuro. El color dominante o el LQIP se adaptan solos a la imagen, no al tema.

Qué técnica elegir

  • Blog o web de contenido con Next.js: placeholder="blur" para imágenes locales y blurDataURL guardado en el CMS para las remotas.
  • Tienda online con cuadrículas de producto: color dominante en los listados y LQIP en la ficha de producto.
  • App móvil y web que comparten API: BlurHash o ThumbHash en el JSON.
  • Imágenes con transparencia: ThumbHash o LQIP en PNG/WebP.

Preguntas frecuentes

¿Los placeholders mejoran el LCP?

No directamente: el LCP se mide cuando se pinta la imagen final, no el placeholder. Lo que mejoran es la percepción de velocidad y, si van con dimensiones, evitan CLS.

¿Google indexa el placeholder en lugar de la imagen?

No. Google indexa la URL del atributo src o srcset. El placeholder va como fondo CSS o en un canvas, y no compite con la imagen real.

¿Dónde guardo los placeholders?

Junto a los metadatos de la imagen: en tu base de datos, en el frontmatter de tus Markdown o en el CMS. Generarlos en cada petición desperdicia recursos.

Guías relacionadas