{"id":923,"date":"2024-09-25T21:49:21","date_gmt":"2024-09-25T21:49:21","guid":{"rendered":"https:\/\/algocademy.com\/blog\/understanding-the-imgsrc-attribute-a-comprehensive-guide-for-web-developers\/"},"modified":"2026-10-15T09:07:29","modified_gmt":"2026-10-15T09:07:29","slug":"understanding-the-imgsrc-attribute-a-comprehensive-guide-for-web-developers","status":"publish","type":"post","link":"https:\/\/algocademy.com\/blog\/understanding-the-imgsrc-attribute-a-comprehensive-guide-for-web-developers\/","title":{"rendered":"The img src Attribute: Syntax, Paths, srcset and Common Errors"},"content":{"rendered":"<p>The <code>src<\/code> attribute of the <code>&lt;img&gt;<\/code> tag tells the browser which image file to load. It takes a URL, either a path relative to the current page or a full address, and with no usable <code>src<\/code> (and no <code>srcset<\/code>) nothing is shown. People often write it as &#8220;imgsrc&#8221;, but there is no such attribute: it is <code>&lt;img src=\"...\"&gt;<\/code>.<\/p>\n<p>This post covers how to write the path correctly, which other attributes to add, how <code>srcset<\/code> and <code>sizes<\/code> work, and how to debug an image that does not show up.<\/p>\n<h2>Basic syntax<\/h2>\n<pre><code class=\"language-html\">&lt;img src=\"images\/lake.jpg\" alt=\"A lake at sunrise with mist over the water\" width=\"800\" height=\"533\"&gt;\n<\/code><\/pre>\n<p><code>&lt;img&gt;<\/code> is a void element: it has no closing tag. Per MDN, <code>src<\/code> is required unless <code>srcset<\/code> is present, so at least one of the two must exist. The other attributes in the example are the ones you should add almost every time: <code>alt<\/code>, <code>width<\/code> and <code>height<\/code>.<\/p>\n<h2>Writing the src path<\/h2>\n<p>Most &#8220;image not showing&#8221; bugs are path bugs. These are the forms you will use:<\/p>\n<table>\n<thead>\n<tr>\n<th>Form<\/th>\n<th>Example<\/th>\n<th>Resolved against<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Relative to the page<\/td>\n<td><code>images\/lake.jpg<\/code><\/td>\n<td>The folder of the current HTML file<\/td>\n<\/tr>\n<tr>\n<td>Parent folder<\/td>\n<td><code>..\/images\/lake.jpg<\/code><\/td>\n<td>One folder up from the current page<\/td>\n<\/tr>\n<tr>\n<td>Root-relative<\/td>\n<td><code>\/images\/lake.jpg<\/code><\/td>\n<td>The root of the site, whatever the page&#8217;s folder<\/td>\n<\/tr>\n<tr>\n<td>Absolute URL<\/td>\n<td><code>https:\/\/example.com\/lake.jpg<\/code><\/td>\n<td>Nothing; it is used as is<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If a page lives at <code>\/blog\/post.html<\/code>, then <code>images\/lake.jpg<\/code> loads <code>\/blog\/images\/lake.jpg<\/code>, while <code>\/images\/lake.jpg<\/code> loads the file from the site root. Moving a page to another folder breaks the first form but not the second, which is why root-relative paths are popular on larger sites.<\/p>\n<h2>Always write the alt attribute<\/h2>\n<p>The <code>alt<\/code> text is shown when the image fails to load, and screen readers read it aloud. Without it, some screen readers announce the file name instead. Rules that cover most cases:<\/p>\n<ul>\n<li>Describe what the image contributes to the page, not just what it looks like. A chart needs its main takeaway, a button icon needs its action (&#8220;Search&#8221;).<\/li>\n<li>Do not start with &#8220;image of&#8221; or &#8220;picture of&#8221;; assistive technology already says it is an image.<\/li>\n<li>For purely decorative images, use an empty value: <code>alt=\"\"<\/code>. That tells assistive technology to skip it. Leaving the attribute out entirely is different and worse.<\/li>\n<li>If the text next to the image already says the same thing, a short alt or an empty one avoids repetition.<\/li>\n<\/ul>\n<p>Search engines also use alt text and the surrounding text to understand an image, but write it for the person who cannot see the image first.<\/p>\n<h2>Set width and height to avoid layout shift<\/h2>\n<p>Without dimensions, the browser does not know how tall the image is until it downloads, so the text below jumps down when the image arrives. Setting <code>width<\/code> and <code>height<\/code> as plain integers (no units) lets the browser compute the aspect ratio and reserve the space in advance. Use the image&#8217;s real pixel dimensions, then let CSS scale it:<\/p>\n<pre><code class=\"language-html\">&lt;img src=\"lake.jpg\" alt=\"Lake at sunrise\" width=\"800\" height=\"533\"&gt;\n<\/code><\/pre>\n<pre><code class=\"language-css\">img {\n  max-width: 100%;\n  height: auto;\n}\n<\/code><\/pre>\n<p>With <code>height: auto<\/code> the image keeps its proportions when it shrinks, and the width\/height attributes still provide the aspect ratio for the reserved space. A common mistake is to put wrong numbers in the attributes (for example 300&#215;200 for a 3:2 image that is really 4:3); the image then either stretches or the reserved box is the wrong shape.<\/p>\n<h2>Responsive images with srcset and sizes<\/h2>\n<p>A phone with a 400 px wide layout should not download a 2400 px photo. <code>srcset<\/code> gives the browser a list of files, and <code>sizes<\/code> tells it how wide the image will be displayed so it can choose.<\/p>\n<pre><code class=\"language-html\">&lt;img\n  src=\"lake-800.jpg\"\n  srcset=\"lake-400.jpg 400w, lake-800.jpg 800w, lake-1200.jpg 1200w\"\n  sizes=\"(min-width: 1000px) 600px, 100vw\"\n  width=\"1200\" height=\"800\"\n  alt=\"Lake at sunrise\"&gt;\n<\/code><\/pre>\n<p>How to read it:<\/p>\n<ul>\n<li>Each <code>w<\/code> value is the intrinsic pixel width of that file, not a screen width. It must match the real file.<\/li>\n<li><code>sizes<\/code> is a list of conditions and widths. Here: on viewports 1000 px or wider the image is shown 600 px wide, otherwise it fills the viewport (<code>100vw<\/code>). The default is <code>100vw<\/code> if you omit it.<\/li>\n<li>The browser takes the display width, multiplies by the device pixel ratio, and picks a file that is big enough. A 400 px layout on a 2x screen needs about 800 px, so it picks <code>lake-800.jpg<\/code>.<\/li>\n<li>With <code>w<\/code> descriptors, browsers that support <code>srcset<\/code> ignore <code>src<\/code>; it is only the fallback for old browsers.<\/li>\n<li>Do not mix <code>w<\/code> and <code>x<\/code> descriptors in one <code>srcset<\/code>.<\/li>\n<\/ul>\n<p>For fixed-size images such as a logo, use pixel density descriptors instead and skip <code>sizes<\/code>:<\/p>\n<pre><code class=\"language-html\">&lt;img src=\"logo.png\" srcset=\"logo.png 1x, logo@2x.png 2x\" width=\"120\" height=\"40\" alt=\"Company logo\"&gt;\n<\/code><\/pre>\n<p>The browser decides which candidate to download, so you cannot force a particular file. That is intended: it can take connection and user settings into account.<\/p>\n<h2>The picture element: art direction and formats<\/h2>\n<p>Use <code>&lt;picture&gt;<\/code> when you want a different crop for different screens (art direction) or want to offer modern formats with a fallback. The inner <code>&lt;img&gt;<\/code> is still required and carries the <code>alt<\/code> text.<\/p>\n<pre><code class=\"language-html\">&lt;picture&gt;\n  &lt;source media=\"(min-width: 800px)\" srcset=\"hero-wide.avif\" type=\"image\/avif\"&gt;\n  &lt;source media=\"(min-width: 800px)\" srcset=\"hero-wide.jpg\"&gt;\n  &lt;source srcset=\"hero-square.jpg\"&gt;\n  &lt;img src=\"hero-square.jpg\" alt=\"Developer at a desk with two monitors\" width=\"800\" height=\"800\"&gt;\n&lt;\/picture&gt;\n<\/code><\/pre>\n<p>The browser uses the first <code>&lt;source&gt;<\/code> whose <code>media<\/code> and <code>type<\/code> it matches, so order matters. If you only need smaller files for smaller screens, plain <code>srcset<\/code> on <code>&lt;img&gt;<\/code> is simpler.<\/p>\n<h2>Loading behavior: lazy, decoding and priority<\/h2>\n<ul>\n<li><code>loading=\"lazy\"<\/code> delays loading until the image is near the viewport. Use it for images below the fold. The default is <code>eager<\/code>. Always set <code>width<\/code> and <code>height<\/code> on lazy images, because an unloaded image has no size and the browser cannot tell when it should load.<\/li>\n<li>Do not lazy-load the main image visible at the top of the page (often the largest element on screen); that delays it. Give it <code>fetchpriority=\"high\"<\/code> instead, which is a hint for the browser to fetch it earlier.<\/li>\n<li><code>decoding=\"async\"<\/code> lets the browser paint other content without waiting for the image to decode. The difference is usually small for ordinary images.<\/li>\n<\/ul>\n<pre><code class=\"language-html\">&lt;!-- top of page --&gt;\n&lt;img src=\"hero.jpg\" alt=\"...\" width=\"1200\" height=\"600\" fetchpriority=\"high\"&gt;\n\n&lt;!-- further down --&gt;\n&lt;img src=\"team.jpg\" alt=\"...\" width=\"600\" height=\"400\" loading=\"lazy\" decoding=\"async\"&gt;\n<\/code><\/pre>\n<h2>Security and privacy notes<\/h2>\n<ul>\n<li><strong>Hotlinking.<\/strong> Pointing <code>src<\/code> at an image on someone else&#8217;s server uses their bandwidth, and they can change or remove the file, or block your site. Host the images you rely on yourself or on a CDN you control.<\/li>\n<li><strong>Referrer.<\/strong> By default browsers send the <code>Referer<\/code> header for cross-origin image requests with the policy <code>strict-origin-when-cross-origin<\/code>, which sends only your origin. To send nothing, add <code>referrerpolicy=\"no-referrer\"<\/code>.<\/li>\n<li><strong>CORS.<\/strong> If you draw a cross-origin image onto a <code>&lt;canvas&gt;<\/code> and read pixels back, you need <code>crossorigin=\"anonymous\"<\/code> on the image and a matching <code>Access-Control-Allow-Origin<\/code> header on the server; otherwise the canvas is tainted.<\/li>\n<li><strong>User-supplied URLs.<\/strong> If a user can set an image URL (a profile picture link, say), remember that your visitors&#8217; browsers will request it, which reveals their IP address to that server. Prefer uploading and re-hosting the file.<\/li>\n<li><strong>HTTPS.<\/strong> Load images over HTTPS; on an HTTPS page, browsers may block or upgrade plain HTTP images.<\/li>\n<\/ul>\n<h2>Setting src from JavaScript<\/h2>\n<p>You can change the source at runtime, and handle failures with the <code>error<\/code> event:<\/p>\n<pre><code class=\"language-javascript\">const img = document.querySelector(\"#avatar\");\nimg.addEventListener(\"error\", () =&gt; {\n  img.src = \"\/images\/default-avatar.png\";\n}, { once: true });\nimg.src = userProvidedUrl;\n<\/code><\/pre>\n<p>The <code>{ once: true }<\/code> option matters: if the fallback also fails, an unguarded handler would set the same source again and loop. If you build the markup as a string, as in many tutorials that use <code>innerHTML<\/code>, make sure the URL is not raw user input; create the element with <code>document.createElement(\"img\")<\/code> and assign <code>src<\/code> instead. For more on the DOM and JavaScript basics, see <a href=\"https:\/\/algocademy.com\/blog\/introduction-to-web-development-basics-html-css-and-javascript\/\">our introduction to web development basics<\/a>.<\/p>\n<h2>Image CDNs<\/h2>\n<p>Services such as Cloudinary, Imgix or Cloudflare Images store your originals and resize or convert them through URL parameters, which pairs well with <code>srcset<\/code>: each width in the list is just the same image with a different parameter. They are a convenience, not a requirement. For a small site, exporting three or four sizes at build time works fine. Check the provider&#8217;s current docs for their URL syntax and pricing, since both change.<\/p>\n<h2>Why your image is not showing<\/h2>\n<p>Go through these in order:<\/p>\n<ol>\n<li><strong>Open the image URL directly.<\/strong> Copy the resolved URL from the Network tab of your browser&#8217;s developer tools and open it in a new tab. A 404 means the path is wrong; a 403 means permissions.<\/li>\n<li><strong>Check the path form.<\/strong> Is it relative to the page or the site root? A leading slash changes the meaning (see the table above).<\/li>\n<li><strong>Check the case and the extension.<\/strong> Many servers run on Linux, where <code>Lake.JPG<\/code> and <code>lake.jpg<\/code> are different files, even if it worked on your Windows or macOS machine. Also check for spaces in file names; use hyphens.<\/li>\n<li><strong>Check the file is really deployed.<\/strong> It exists locally but was not committed, uploaded or copied to the build output.<\/li>\n<li><strong>Mixed content.<\/strong> An <code>http:\/\/<\/code> image on an <code>https:\/\/<\/code> page may be blocked.<\/li>\n<li><strong>Hidden by CSS.<\/strong> An image can load but be invisible because of <code>display: none<\/code>, zero height in a flex or grid parent, or <code>max-height<\/code>. Inspect the element and look at its computed size.<\/li>\n<li><strong>Wrong <code>srcset<\/code>.<\/strong> If one of the candidate files 404s, only some screens show a broken image. Test with different window widths and with the device toolbar.<\/li>\n<li><strong>Wrong content type or a corrupt file.<\/strong> A server that sends an HTML error page with status 200 will show a broken icon. Check the response in the Network tab.<\/li>\n<\/ol>\n<h2>Quick checklist<\/h2>\n<ul>\n<li>Correct <code>src<\/code> path, tested from the deployed page.<\/li>\n<li>Meaningful <code>alt<\/code>, or <code>alt=\"\"<\/code> for decoration.<\/li>\n<li>Real <code>width<\/code> and <code>height<\/code>, plus <code>max-width: 100%; height: auto<\/code> in CSS.<\/li>\n<li><code>srcset<\/code> and <code>sizes<\/code> for large content images; <code>loading=\"lazy\"<\/code> below the fold.<\/li>\n<li>Modern formats such as WebP or AVIF through <code>&lt;picture&gt;<\/code> when file size matters, with a JPEG or PNG fallback.<\/li>\n<li>Images hosted by you, served over HTTPS.<\/li>\n<\/ul>\n<p>To practice HTML, CSS and JavaScript by building things step by step, you can try <a href=\"https:\/\/algocademy.com\/\">AlgoCademy<\/a>. A good exercise is to take a page with one large photo and add dimensions, <code>srcset<\/code>, and lazy loading, then compare the Network tab before and after.<\/p>\n<h2>Frequently asked questions<\/h2>\n<h3>Is there an imgsrc attribute?<\/h3>\n<p>No. The attribute is <code>src<\/code> on the <code>&lt;img&gt;<\/code> element. &#8220;imgsrc&#8221; is just a common way to refer to it in searches.<\/p>\n<h3>Is alt required?<\/h3>\n<p>For accessibility, yes. Use descriptive text for meaningful images and <code>alt=\"\"<\/code> for decorative ones.<\/p>\n<h3>Can I use srcset without sizes?<\/h3>\n<p>With <code>x<\/code> descriptors, yes. With <code>w<\/code> descriptors, <code>sizes<\/code> defaults to <code>100vw<\/code>, which is wrong for any image narrower than the full viewport and can make the browser download a larger file than needed.<\/p>\n<h3>Should every image be lazy-loaded?<\/h3>\n<p>No. Lazy-load images below the fold, and load the main visible image at the top of the page normally.<\/p>\n<h3>What is the difference between srcset on img and the picture element?<\/h3>\n<p><code>srcset<\/code> lets the browser pick among sizes of the same image. <code>&lt;picture&gt;<\/code> lets you control the choice with media queries and file types, for different crops or formats.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How the HTML img src attribute works: relative and absolute paths, alt text, width and height, srcset and sizes, lazy loading, and fixes for broken images.<\/p>\n","protected":false},"author":1,"featured_media":912,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23],"tags":[],"class_list":["post-923","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-problem-solving"],"_links":{"self":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/923"}],"collection":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/comments?post=923"}],"version-history":[{"count":2,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/923\/revisions"}],"predecessor-version":[{"id":8665,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/923\/revisions\/8665"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media\/912"}],"wp:attachment":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media?parent=923"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/categories?post=923"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/tags?post=923"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}