Một thống kê HTTP Archive 2025 cho thấy hơn 60% website nhiều ảnh vẫn gửi cùng một file ảnh cho mọi thiết bị: điện thoại 360px và màn hình 4K nhận y hệt nhau. Hậu quả là người dùng di động tải ảnh lớn gấp 3-8 lần kích thước màn hình họ có thể hiển thị, tốn băng thông và làm chậm thời gian tải trang. Đây chính là vấn đề mà srcset và sizes sinh ra để giải quyết.
Bài này tập trung vào kích thước và độ phân giải ảnh, không bàn về định dạng. Nếu bạn cần chọn giữa WebP, AVIF hay JPEG, hãy xem bài so sánh WebP và AVIF riêng. Hai chuyện này độc lập: bạn vẫn có thể (và nên) dùng cả hai cùng lúc.
srcset với width descriptor hoạt động thế nào
Cách phổ biến nhất là khai báo nhiều file ảnh kèm width descriptor (mô tả chiều rộng — số nguyên kèm chữ w, là chiều rộng pixel thật của file):
<img
src="anh-800.jpg"
srcset="anh-400.jpg 400w,
anh-800.jpg 800w,
anh-1200.jpg 1200w,
anh-1600.jpg 1600w"
sizes="(min-width: 1024px) 50vw, 100vw"
width="1600"
height="900"
alt="Mô tả ảnh thật">Ở đây srcset liệt kê các phiên bản, còn src vẫn giữ một ảnh dự phòng cho trình duyệt quá cũ. Trình duyệt sẽ tự tính xem cần file nào dựa trên sizes, kích thước viewport và mật độ điểm ảnh của màn hình (DPR — Device Pixel Ratio, tỉ lệ pixel vật lý trên pixel CSS).

sizes — mảnh ghép bắt buộc, và hay bị làm sai
Khi dùng width descriptor, sizes là bắt buộc. Nó cho trình duyệt biết ảnh sẽ chiếm bao nhiêu chiều rộng trên trang ở từng ngưỡng màn hình — vì trình duyệt phải chọn ảnh trước khi CSS được áp dụng đầy đủ.
Cú pháp là danh sách cặp “điều kiện màn hình → chiều rộng hiển thị”, giá trị cuối không có điều kiện đóng vai trò mặc định:
sizes="(min-width: 1024px) 50vw,
(min-width: 640px) 75vw,
100vw"Đọc là: màn hình từ 1024px trở lên ảnh rộng 50% viewport; từ 640px là 75%; còn lại (di động) chiếm trọn 100%. Lỗi thường gặp nhất là để sizes="100vw" trong khi thực tế ảnh nằm trong cột chỉ rộng nửa trang — trình duyệt sẽ tải file to gấp đôi cần thiết. Khi sửa layout, đừng quên cập nhật lại sizes cho khớp.
sizes=auto cho ảnh lazy-load
Với ảnh tải lười (lazy-load — chỉ tải khi cuộn tới), trình duyệt hiện đại hỗ trợ sizes="auto". Khi đó trình duyệt tự lấy chiều rộng bố cục thật của ảnh để chọn file, miễn bạn không phải đoán mò:
<img sizes="auto"
srcset="anh-400.jpg 400w, anh-800.jpg 800w"
src="anh-800.jpg" width="800" height="450" alt="...">Density descriptor — khi nào dùng x thay cho w
Nếu ảnh luôn hiển thị cùng một kích thước CSS cố định (icon, logo, avatar), bạn không cần sizes mà dùng density descriptor (mô tả mật độ điểm ảnh, chữ x):
<img src="logo.png"
srcset="logo.png 1x, logo-2x.png 2x"
width="120" height="40" alt="Logo">Lưu ý quan trọng: không trộn w và x trong cùng một srcset — chọn một kiểu. Ngoài ra, với ảnh nội dung thường, làm tới 2x là đủ; 3x hầu như không cải thiện cảm nhận thị giác mà file lại nặng thêm đáng kể.
picture — khi srcset chưa đủ
Phần tử <picture> giải quyết hai việc mà srcset đơn thuần không làm được:
1. Art direction (định hướng nghệ thuật — đổi khung hình theo màn hình). Trên desktop bạn muốn ảnh ngang rộng, trên di động muốn cắt vuông lấy chủ thể ở giữa — đây là hai ảnh được crop khác nhau, không chỉ khác kích thước:
<picture>
<source media="(max-width: 640px)" srcset="anh-vuong.jpg">
<img src="anh-ngang.jpg" width="1600" height="900" alt="...">
</picture>2. Chuyển định dạng có fallback. Cho trình duyệt thử AVIF trước, rồi WebP, cuối cùng rơi về JPEG:
<picture>
<source type="image/avif" srcset="anh.avif">
<source type="image/webp" srcset="anh.webp">
<img src="anh.jpg" width="1200" height="675" alt="...">
</picture>Trình duyệt lấy <source> đầu tiên mà nó hiểu. Bạn cũng có thể gắn srcset nhiều width + sizes vào mỗi <source> để gộp cả hai kỹ thuật.
Đừng quên width và height — chống layout shift
Dù dùng kỹ thuật nào, luôn khai width và height (hoặc aspect-ratio trong CSS). Trình duyệt dùng tỉ lệ này để chừa chỗ trước, tránh nội dung nhảy khi ảnh tải xong — yếu tố trực tiếp ảnh hưởng CLS (Cumulative Layout Shift — tổng dịch chuyển bố cục, ngưỡng “tốt” ≤ 0.1 đo ở phân vị 75). Chủ đề này được đào sâu trong bài khai aspect-ratio để fix CLS.

Tác động lên Core Web Vitals và bảng tóm tắt
Responsive image đúng cách giúp ảnh LCP (Largest Contentful Paint — phần tử lớn nhất hiện ra, ngưỡng tốt ≤ 2.5s) tải nhẹ và nhanh hơn trên di động. Với ảnh LCP nằm ở khung nhìn đầu, nên thêm fetchpriority="high" và không đặt loading="lazy".
| Tình huống | Dùng | Cần sizes? |
|---|---|---|
| Ảnh nội dung co giãn theo layout | srcset width (w) | Có |
| Logo/icon kích thước cố định | srcset density (x) | Không |
| Đổi crop theo màn hình | <picture> + media | Tuỳ |
| Fallback định dạng | <picture> + type | Tuỳ |
Trong thực tế vận hành web22.dev, Web22 để CDN sinh sẵn 3-5 phiên bản theo bước kích thước file (400/800/1200/1600px) thay vì theo từng thiết bị — vừa đủ phủ các màn hình phổ biến mà không phình số lượng file phải quản. WordPress mặc định đã sinh srcset; việc của bạn chủ yếu là chỉnh lại sizes cho khớp layout thật.
Câu hỏi thường gặp
srcset có cần CSS không? Không. srcset/sizes là HTML thuần, trình duyệt xử lý độc lập với CSS. Nhưng width/height/aspect-ratio thì nên có để giữ bố cục ổn định.
Trình duyệt cũ không hiểu srcset thì sao? Nó bỏ qua srcset và dùng src như ảnh bình thường, nên trang vẫn chạy. Vì vậy luôn để một src hợp lý làm dự phòng.
Nên dùng srcset hay picture? Mặc định dùng srcset + sizes. Chỉ nâng lên <picture> khi cần đổi khung hình hoặc chuyển định dạng có fallback.
Mà srcset chỉ lo phần ảnh. Nếu cả bố cục trang còn vỡ trên màn nhỏ thì xem dịch vụ thiết kế website responsive, làm một thể cho tròn trải nghiệm điện thoại.
Nếu site của bạn nặng ảnh và muốn rà soát toàn bộ srcset, sizes lẫn định dạng cho khớp Core Web Vitals, có thể tham khảo dịch vụ tối ưu tốc độ website của Web22.
