Thumbnails are the most important visual element on a video platform. On TopVideoHub, every page displays 20-50 thumbnails. Optimizing them has a massive impact on page load time and Core Web Vitals.
Here's the complete thumbnail optimization strategy I use.
YouTube Thumbnail Sizes
YouTube provides thumbnails at multiple sizes:
| Quality | File | Dimensions | Typical Size |
|---|---|---|---|
| default | default.jpg |
120x90 | ~3 KB |
| medium | mqdefault.jpg |
320x180 | ~12 KB |
| high | hqdefault.jpg |
480x360 | ~25 KB |
| standard | sddefault.jpg |
640x480 | ~40 KB |
| maxres | maxresdefault.jpg |
1280x720 | ~120 KB |
The URL pattern is:
https://i.ytimg.com/vi/{VIDEO_ID}/{QUALITY}.jpg
Choosing the Right Size
The key principle: serve the smallest thumbnail that looks sharp at the display size.
On a video grid, cards are typically 280-320px wide. Serving maxresdefault.jpg (1280px) for a 300px card wastes 90% of the data.
class ThumbnailHelper {
public static function url(
string $videoId,
string $context = 'grid'
): string {
$quality = match($context) {
'hero' => 'maxresdefault', // Full-width hero: needs max quality
'grid' => 'mqdefault', // Grid card: 320px is enough
'sidebar' => 'default', // Sidebar: tiny, minimal quality
'og' => 'hqdefault', // Open Graph meta: medium quality
default => 'mqdefault',
};
return "https://i.ytimg.com/vi/{$videoId}/{$quality}.jpg";
}
public static function srcset(string $videoId): string {
return sprintf(
'%s 320w, %s 480w, %s 640w',
self::url($videoId, 'grid'),
"https://i.ytimg.com/vi/{$videoId}/hqdefault.jpg",
"https://i.ytimg.com/vi/{$videoId}/sddefault.jpg"
);
}
}
Responsive Images with srcset
Use srcset and sizes to let the browser choose the optimal thumbnail:
<img src="<?= ThumbnailHelper::url($video['id'], 'grid') ?>"
srcset="<?= ThumbnailHelper::srcset($video['id']) ?>"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 50vw, 320px"
alt="<?= htmlspecialchars($video['title']) ?>"
width="320" height="180"
loading="lazy"
decoding="async">
Key attributes:
-
sizestells the browser the display width at each breakpoint -
loading="lazy"defers off-screen images -
decoding="async"prevents thumbnails from blocking rendering -
widthandheightprevent layout shift (CLS)
WebP Detection and Fallback
YouTube's CDN also serves WebP format, which is 25-35% smaller than JPEG:
https://i.ytimg.com/vi_webp/{VIDEO_ID}/mqdefault.webp
Use <picture> for WebP with JPEG fallback:
<picture>
<source type="image/webp"
srcset="https://i.ytimg.com/vi_webp/<?= $videoId ?>/mqdefault.webp">
<img src="https://i.ytimg.com/vi/<?= $videoId ?>/mqdefault.jpg"
alt="<?= htmlspecialchars($title) ?>"
width="320" height="180"
loading="lazy"
decoding="async">
</picture>
Handling Missing Thumbnails
Not all videos have maxresdefault. YouTube returns a placeholder for missing resolutions:
class ThumbnailValidator {
private static array $cache = [];
public static function bestAvailable(string $videoId): string {
if (isset(self::$cache[$videoId])) {
return self::$cache[$videoId];
}
// Try maxres first, fall back to lower qualities
$qualities = ['maxresdefault', 'sddefault', 'hqdefault', 'mqdefault'];
foreach ($qualities as $q) {
$url = "https://i.ytimg.com/vi/{$videoId}/{$q}.jpg";
$headers = @get_headers($url, true);
if ($headers && str_contains($headers[0], '200')) {
self::$cache[$videoId] = $url;
return $url;
}
}
// Fallback to medium quality (always exists)
$fallback = "https://i.ytimg.com/vi/{$videoId}/mqdefault.jpg";
self::$cache[$videoId] = $fallback;
return $fallback;
}
}
Note: Don't call this on every page load. Run it during the fetch cron and store the best quality in the database.
Placeholder and Blur-Up
For perceived performance, show a tiny blurred placeholder before the real thumbnail loads:
.video-thumb {
background-color: #1a1a2e;
background-size: cover;
transition: filter 0.3s;
}
.video-thumb[data-loaded="false"] {
filter: blur(10px);
}
.video-thumb[data-loaded="true"] {
filter: none;
}
document.querySelectorAll('.video-thumb img').forEach(img => {
if (img.complete) {
img.closest('.video-thumb').dataset.loaded = 'true';
} else {
img.addEventListener('load', () => {
img.closest('.video-thumb').dataset.loaded = 'true';
});
}
});
Impact on TopVideoHub
After implementing all these optimizations on TopVideoHub:
- Page weight for thumbnails dropped by ~60%
- LCP improved from 2.1s to 1.1s
- CLS dropped to near zero
- Mobile PageSpeed score increased from 71 to 85+
Top comments (0)