惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
P
Palo Alto Networks Blog
月光博客
月光博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
InfoQ
aimingoo的专栏
aimingoo的专栏
腾讯CDC
T
Threatpost
D
DataBreaches.Net
Vercel News
Vercel News
F
Fortinet All Blogs
Engineering at Meta
Engineering at Meta
C
Cybersecurity and Infrastructure Security Agency CISA
Forbes - Security
Forbes - Security
U
Unit 42
C
Check Point Blog
Blog — PlanetScale
Blog — PlanetScale
O
OpenAI News
量子位
TaoSecurity Blog
TaoSecurity Blog
Microsoft Azure Blog
Microsoft Azure Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Visual Studio Blog
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
Security Archives - TechRepublic
Security Archives - TechRepublic
The Last Watchdog
The Last Watchdog
S
Security Affairs
Attack and Defense Labs
Attack and Defense Labs
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
Microsoft Security Blog
Microsoft Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
小众软件
小众软件
S
SegmentFault 最新的问题
www.infosecurity-magazine.com
www.infosecurity-magazine.com
W
WeLiveSecurity
AI
AI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
I
Intezer
Know Your Adversary
Know Your Adversary
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
博客园_首页
NISL@THU
NISL@THU
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
srcset and sizes: How the Browser Picks Images
Ashish Kumar · 2026-05-09 · via DEV Community

Related: What loading="lazy" Does and Why Google Added It to the HTML Spec covers the other half of image loading behavior that pairs with responsive image selection.

A user on a 375px iPhone with a 3x retina display viewing your site on 4G should get a different image file than a user on a 1440px desktop with a 1x monitor on a slow hotel WiFi connection. These two users have completely different needs: one wants a sharp image at a small size, the other wants a smaller file at a large size. Without srcset and sizes, they both get the same file, and at least one of them is getting it wrong.

What this covers: Why a single image URL cannot serve all devices correctly, what the srcset and sizes attributes actually tell the browser, how the browser's selection algorithm works, and the common mistakes that make the whole system silently fail.

Diagram showing how the browser uses srcset and sizes to select the optimal image based on viewport width and device pixel ratio.


The problem with a single image URL

When you write <img src="/hero.jpg">, every visitor to your page downloads the same file regardless of their device, screen size, or connection speed.

For a full-bleed hero image that looks good on a 4K monitor, you might need a 2400px wide image. On a 375px phone, that same image takes four times more bandwidth to download than needed. The phone then scales it down to 375px, discarding all that extra data. You paid for data that was thrown away.

The inverse is also a problem. If you serve a small image to save bandwidth, it looks blurry on high-density displays like Retina screens because those screens pack 2x or 3x more pixels into the same physical space and need a proportionally larger image to fill them sharply.

<!-- Every device gets the same 2400px image. Wrong. -->
![Hero image](https://renderlog.in/hero-2400.jpg)

Enter fullscreen mode Exit fullscreen mode

The solution is not to pick one compromise image. The solution is to tell the browser about all your image options and let it pick the right one based on information it already has: the viewport size, the device pixel ratio, and the connection speed.


What srcset actually tells the browser

The srcset attribute is a comma-separated list of image candidates. Each candidate has a URL and a width descriptor that tells the browser how wide that image file is in pixels.

![Hero image](https://renderlog.in/hero-800.webp)

Enter fullscreen mode Exit fullscreen mode

The w unit after each number is the intrinsic width of that image file. It is not the display width. It is saying: "this file, when you open it, is 800 pixels wide." That is a fact about the file, not an instruction to the browser.

The src attribute is the fallback for browsers that do not understand srcset. Old browsers ignore srcset and use src instead. Always include it.

Without a sizes attribute, the browser assumes the image will be displayed at 100% of the viewport width. That assumption is wrong for almost every real layout, which is why sizes exists.


What sizes tells the browser

sizes tells the browser how wide the image will actually appear on screen at different viewport sizes. It uses the same media query syntax as CSS.

![Hero image](https://renderlog.in/hero-800.webp)

Enter fullscreen mode Exit fullscreen mode

Reading the sizes value here: if the viewport is 768px or narrower, the image takes up 100% of the viewport width. If the viewport is between 768px and 1200px, the image takes up 50% of the viewport width. Otherwise, the image is exactly 1200px wide (capped by a max-width container).

The browser reads sizes before it has applied any CSS. This is intentional. The browser preloader runs very early in the parsing process, before stylesheets are downloaded, to get image requests into the network queue as soon as possible. It cannot wait for CSS to be parsed to know image display sizes. So you, the developer, tell it the answer ahead of time through sizes.


The selection algorithm

Given srcset and sizes, the browser calculates which image to fetch like this:

  1. Evaluate the sizes conditions against the current viewport width to get the rendered width (call it W)
  2. Multiply W by the device pixel ratio (DPR) to get the needed pixels (call it P)
  3. Pick the smallest image from srcset that is at least P pixels wide

For a concrete example: viewport is 375px wide, DPR is 3x, sizes says 100vw at this breakpoint.

  • Rendered width W = 375px
  • Needed pixels P = 375 x 3 = 1125px
  • Available candidates: 400w, 800w, 1200w, 2400w
  • Smallest that is at least 1125px wide: 1200w
  • Browser fetches /hero-1200.webp

Without srcset and sizes, that same device would fetch /hero-2400.webp, downloading 4x more data than it needs to render a sharp image.

On a 1440px desktop with 1x DPR where the image appears at 50% viewport width:

  • Rendered width W = 720px
  • Needed pixels P = 720 x 1 = 720px
  • Smallest candidate at least 720px: 800w
  • Browser fetches /hero-800.webp

A desktop user on a 1x monitor is downloading a smaller file than an iPhone user, because the desktop user does not need the extra pixels that a retina display requires.


The picture element for format switching

srcset on a plain <img> handles size selection but not format selection. For that, you use the <picture> element, which lets you offer different formats with explicit priority order.

<picture>
  <source
    type="image/avif"
    srcset="
      /hero-400.avif   400w,
      /hero-800.avif   800w,
      /hero-1200.avif 1200w
    "
    sizes="(max-width: 768px) 100vw, 1200px"
  />
  <source
    type="image/webp"
    srcset="
      /hero-400.webp   400w,
      /hero-800.webp   800w,
      /hero-1200.webp 1200w
    "
    sizes="(max-width: 768px) 100vw, 1200px"
  />
  ![Hero image](https://renderlog.in/hero-1200.jpg)
</picture>

Enter fullscreen mode Exit fullscreen mode

The browser works through <source> elements in order, picks the first one it supports, and applies the same size selection algorithm from srcset. A browser that supports AVIF gets AVIF at the right size. A browser that does not falls through to WebP. If neither is supported, the <img> fallback loads the JPEG.


Common mistakes that make this silently fail

Using pixel density descriptors instead of width descriptors. There is a second descriptor syntax that uses x instead of w, like srcset="/hero-2x.jpg 2x". This only handles DPR, not viewport size. It is the older syntax and far less flexible. Use w descriptors with sizes instead.

<!-- Older approach: only handles DPR, ignores viewport size -->
![Hero](https://renderlog.in/hero.jpg)

<!-- Better: handles both DPR and viewport size -->
![Hero](https://renderlog.in/hero-800.jpg)

Enter fullscreen mode Exit fullscreen mode

Wrong sizes values. If your sizes attribute says 100vw but the image is actually displayed at 50% of the viewport inside a two-column layout, the browser will fetch a file twice as large as needed. Get the rendered width right, even if it means inspecting the layout in DevTools to measure the actual computed width at each breakpoint.

Skipping sizes entirely. Without sizes, the browser assumes the image is 100vw. On a 1440px desktop with a two-column layout where your image is actually 600px wide, the browser fetches a 1440px image. The default assumption is almost always too large for content images.

Not including the image dimensions. Without width and height on the <img> element, the browser cannot reserve space for the image before it loads. When the image arrives, the page reflows and you get Cumulative Layout Shift. This applies whether you use srcset or not, but it is especially visible with lazy-loaded responsive images because the load is intentionally deferred.


What about CSS background images?

srcset and sizes only work on <img> elements and <source> elements inside <picture>. CSS background images do not benefit from this system at all.

For responsive CSS background images, the only native option is image-set():

.hero {
  background-image: image-set(
    url('/hero-800.webp') 1x,
    url('/hero-1600.webp') 2x
  );
}

Enter fullscreen mode Exit fullscreen mode

image-set() is less flexible than srcset because it only handles DPR, not viewport size. For hero sections or any prominent image, using an <img> element with proper srcset and sizes is almost always the better choice over a CSS background image.


Generating responsive images in practice

Manually creating and maintaining multiple resized versions of every image is not practical at scale. The standard approach is to generate them automatically:

Vite and Astro both have image optimization plugins that generate multiple sizes at build time from a single source image. Astro's <Image> component handles srcset generation automatically when you specify a widths prop.

Next.js provides an <Image> component that generates srcset and handles format selection automatically, including on-demand resizing through its image optimization API.

CDN image optimization services like Cloudinary, Imgix, and Cloudflare Images let you request any size via URL parameters and serve the optimal format based on the Accept header. You store one original image, the CDN handles the rest.

The common thread is that the developer should not be manually maintaining five versions of every image. The build tool or CDN should generate them. What the developer needs to understand is srcset and sizes so they know what the tool is doing and can write the right configuration.


The performance case in numbers

Device Without srcset With srcset Savings
iPhone 13 (375px, 3x) 2400px image, ~420KB 1200px image, ~95KB 77%
iPad (768px, 2x) 2400px image, ~420KB 1600px image, ~165KB 61%
1080p desktop (1x) 2400px image, ~420KB 1200px image, ~95KB 77%
4K desktop (2x) 2400px image, ~420KB 2400px image, ~420KB 0%

The only device in this table that should download the full 2400px image is the 4K display with a 2x DPR where the image fills the viewport. Every other device is paying for data that gets discarded after the browser scales the image down. srcset and sizes stop that from happening.


Read the original article on Renderlog.in:
https://renderlog.in/blog/srcset-sizes-responsive-images-explained/

If you found this helpful, I've also built some free tools for developers and everyday users. Feel free to try them once:

JSON Tools: https://json.renderlog.in
Text Tools: https://text.renderlog.in
QR Tools: https://qr.renderlog.in