Image Placeholder Techniques Compared - LQIP, BlurHash, and SQIP Implementation Guide
What Are Image Placeholders - Why They're Better Than Blank Space
Image placeholders are alternative content displayed while the actual image is loading. On pages implementing lazy loading, images outside the viewport have their loading deferred, which can cause momentary blank spaces during scrolling. Filling these blanks with placeholders significantly improves user experience.
Problems without placeholders:
- CLS (Cumulative Layout Shift) degradation: Layout shifts when images finish loading, lowering Core Web Vitals scores
- Perceived slowness: Blank areas make users feel the page is broken or loading slowly
- Loss of content context: Users cannot predict what will appear in the area until the image loads
Placeholder types fall into four main categories:
- Solid color placeholder: Fills with the image's dominant color. Simplest but provides minimal information
- LQIP (Low Quality Image Placeholder): Embeds a tiny low-quality version of the image inline
- BlurHash: Encodes the image as a compact string, generating a blurred image client-side
- SQIP (SVG-based Quality Image Placeholder): Approximates the image using SVG primitive shapes
The choice depends on balancing data size, visual quality, implementation complexity, and performance impact. The following sections compare each technique in detail.
Implementing LQIP - Preview Display with Tiny Images
LQIP (Low Quality Image Placeholder) embeds an extremely downsized, low-quality version of the original image inline in HTML. It became widely known after Facebook adopted it around 2015. When browsers upscale the tiny image, natural blur effects occur, making it function as a placeholder without additional CSS filters.
Implementation steps:
- Resize the original image to approximately 20-42 pixels wide (maintaining aspect ratio)
- Encode as JPEG at quality 20-30
- Base64 encode and set as
data:image/jpeg;base64,...in the<img>tag'ssrc - Replace
srcwith the actual image once loaded
Data size guidelines: A 20px-wide JPEG (quality 20) is approximately 300-600 bytes. Base64 encoded, this becomes about 400-800 characters. Even embedded directly in HTML, the impact per image stays under 1KB.
Enhanced blur with CSS: Browser bilinear interpolation alone may show visible block artifacts. Applying filter: blur(20px) and transitioning it off when the image loads produces smoother results:
.lqip { filter: blur(20px); transform: scale(1.1); transition: filter 0.3s, transform 0.3s; }.lqip.loaded { filter: blur(0); transform: scale(1); }
LQIP's advantages are implementation simplicity and no special library requirements. It's easily generated with Sharp or ImageMagick and integrates smoothly into existing build pipelines.
How BlurHash Works - An Innovative String-Based Image Representation
BlurHash is an algorithm developed by Wolt (a Finnish food delivery company) that encodes images into short strings of approximately 20-30 characters. Since blurred images are generated client-side from this string, there's no need to transfer image data like LQIP does.
Encoding principle: BlurHash decomposes images into frequency components using Discrete Cosine Transform (DCT), retaining only low-frequency components. The number of components (X direction x Y direction) is configurable, defaulting to 4x3. Each component's coefficients are encoded in Base83 to produce the string.
Decoding flow:
- Decode the Base83 string to recover DCT coefficients
- Calculate inverse DCT at the specified resolution (typically around 32x32)
- Convert each pixel's color values to sRGB and render to Canvas or ImageData
Data size: With 4x3 components, just 20 characters (approximately 20 bytes) can represent the image's general color distribution. This is 10-30x more compact than LQIP's 300-600 bytes. Including it in JSON API responses has virtually zero bandwidth impact.
Implementation example (JavaScript decode):
import { decode } from 'blurhash';const pixels = decode('LEHV6nWB2yk8pyo0adR*.7kCMdnj', 32, 32);// pixels is Uint8ClampedArray (32*32*4 = 4096 elements)// Set as Canvas ImageData to render
BlurHash is adopted by Instagram, Mastodon, Unsplash, and many other services. It's ideal for including hash strings in API responses to display placeholders before image URLs finish loading.
SQIP Features - Artistic Placeholders with SVG Primitives
SQIP (SVG-based Quality Image Placeholder) approximates images using combinations of SVG primitive shapes (triangles, ellipses, rectangles, etc.). Published by Tobias Baldauf in 2017, it internally uses the Primitive algorithm by Michael Fogleman.
Generation process:
- Analyze the original image to identify visually important regions
- Place a specified number of primitive shapes (default 8) to approximate the image
- Optimize each shape's form, position, color, and opacity (hill climbing method)
- Apply a Gaussian blur filter to the generated SVG
- Base64 encode the final SVG for inline embedding
Data size: Approximately 800-1200 bytes with 8 primitives. Similar to LQIP, but since SVG is vector-based, it displays sharply at any resolution. No blurriness on Retina displays.
Visual quality: SQIP's greatest feature is its aesthetic beauty as a placeholder. The combination of geometric shapes creates an artistic impression, producing more design-forward placeholders than simple blurred images. It's well-suited for portfolio and gallery sites where visual quality matters.
SQIP's challenges: Slow generation time is the primary weakness. Processing takes 1-5 seconds per image, significantly increasing build times for sites with many images. Additionally, the Node.js sqip package has stagnating maintenance, occasionally causing compatibility issues with newer Node.js versions.
As an alternative, you can use the primitive command-line tool directly and manually add blur filters to the output SVG. Being implemented in Go, it runs quickly.
Comparison and Selection Criteria - Choosing the Best Technique for Your Project
Here's a comparison of the four placeholder techniques across key evaluation dimensions. Select the optimal technique based on your project's requirements.
Data size comparison:
- Dominant color: 7 bytes (CSS color value only)
- BlurHash: 20-30 bytes (string)
- LQIP: 300-800 bytes (Base64 image)
- SQIP: 800-1500 bytes (Base64 SVG)
Visual quality (information richness): SQIP > LQIP > BlurHash > Dominant color. SQIP best preserves image structure, LQIP naturally represents color distribution. BlurHash reproduces approximate color gradients but loses edge information.
Generation speed: Dominant color (under 1ms) > BlurHash (10-50ms) > LQIP (50-200ms) > SQIP (1-5 seconds). In build pipelines processing many images, SQIP's generation time can become a bottleneck.
Recommended use cases:
- API-driven apps (social media, e-commerce): BlurHash recommended. Implementation requires only including a string in API responses, with minimal data size impact
- Blogs and media sites: LQIP recommended. Generate at build time and embed inline. Simple implementation with sufficient visual quality
- Portfolios and galleries: SQIP recommended. For sites where visual beauty matters, the generation cost is justified
- Performance-first: Dominant color recommended. Minimal data size while fulfilling the basic role of CLS prevention
Combining multiple techniques is also effective. For example, using LQIP for above-the-fold images and only dominant colors for below-the-fold images is a practical graduated approach.
Implementation with Next.js and Astro - Integration with Modern Frameworks
Modern web frameworks provide mechanisms for efficiently integrating placeholder generation and display. Here are concrete implementation methods for Next.js and Astro.
Next.js Image component: Next.js's <Image> component natively supports LQIP and BlurHash via the placeholder prop:
<Image src="/photo.jpg" placeholder="blur" blurDataURL="data:image/jpeg;base64,..." />
For static imports, blurDataURL is automatically generated at build time. For dynamic images, use the plaiceholder library for server-side generation:
import { getPlaiceholder } from 'plaiceholder';const { base64 } = await getPlaiceholder('/photo.jpg');
Astro implementation: Astro's <Image> component doesn't have direct placeholder support, but generating LQIP with Sharp at build time and passing it to components is the common pattern:
import sharp from 'sharp';const buffer = await sharp('src/images/photo.jpg').resize(20).jpeg({ quality: 20 }).toBuffer();const lqip = `data:image/jpeg;base64,${buffer.toString('base64')}`;
BlurHash React implementation: The react-blurhash package easily displays Canvas-based placeholders from BlurHash strings. Combining with Intersection Observer to load actual images when entering the viewport is effective.
Performance considerations: Inline Base64 images increase HTML size, so SSR pages need to watch initial HTML transfer volume. For pages with 50+ images, set only dominant colors for below-the-fold images and limit LQIP to the 5-10 images near the first viewport.