HTMLBeginner

HTML Images In Depth: img, alt, figure and Lazy Loading

Learn the HTML img element properly: src, meaningful alt text, width and height to avoid layout shift, lazy loading, figure/figcaption, and image formats.

All HTML lessons

What you will learn

Images are everywhere on the web, but the <img> tag is one of the most misused elements in HTML — people copy-paste <img src="..."> and stop there. In this lesson you will learn every attribute that actually matters: src, alt (and how to write good alt text, not just any text), width/height and why they matter more than you’d think, loading="lazy", and the <figure>/<figcaption> pair for captioned images.

The basic <img> tag

<img src="mountain.jpg" alt="A mountain covered in snow at sunset">
  • <img> is a void element (you met these in the elements-and-tags lesson) — it never has a closing tag or content, because everything it needs is in its attributes.
  • <img> is inline-level by default (you met this in the block/inline lesson), but unlike <span>, it does accept width and height, because it is a replaced element — the browser replaces the tag with actual visual content.

src: where the image comes from

src (source) can be:

<!-- A relative path to an image in your own project -->
<img src="images/mountain.jpg" alt="A mountain covered in snow at sunset">

<!-- An absolute URL to an image hosted elsewhere -->
<img src="https://example.com/images/mountain.jpg" alt="A mountain covered in snow at sunset">

The same absolute-vs-relative rules from the links lesson apply here: use a relative path for images that live in your own project, and an absolute URL for images hosted somewhere else.

alt: the attribute you must never skip

alt (alternative text) is a written description of the image, shown or spoken in three situations:

  1. The image fails to load (broken link, slow connection, wrong file name) — the browser shows the alt text in its place.
  2. A visitor uses a screen reader — it reads the alt text aloud instead of the image, since a screen reader cannot “see” pictures.
  3. A search engine reads it to understand what the image shows.
<!-- Bad: tells the reader nothing -->
<img src="mountain.jpg" alt="image">
<img src="mountain.jpg" alt="photo1234">
<img src="mountain.jpg" alt="">

<!-- Good: describes what the image actually shows -->
<img src="mountain.jpg" alt="A snow-covered mountain peak against a orange sunset sky">

How to write good alt text

  • Describe what the image shows, as if explaining it to someone on the phone who cannot see it.
  • Keep it reasonably short — one clear sentence, not a paragraph.
  • Don’t start with “Image of…” or “Picture of…” — screen readers already announce it as an image; adding that phrase is redundant.
  • Match the purpose of the image on the page. A product photo’s alt should describe the product, not just “photo”; a person’s headshot on a team page should probably include their name if the surrounding text doesn’t already.
<!-- On a recipe blog -->
<img src="cake.jpg" alt="A three-layer chocolate cake topped with fresh strawberries">

<!-- On a product page -->
<img src="shoe.jpg" alt="Red running shoe with white sole, side view">

Decorative images: alt=""

Some images are purely decorative — a background pattern, a divider graphic, an icon that repeats text already on the page. For these, use an empty alt attribute:

<img src="divider-swirl.png" alt="">

This is not the same as forgetting alt altogether. Writing alt="" on purpose tells screen readers to skip the image entirely, so the visitor isn’t interrupted by a meaningless description like “decorative swirl graphic 47”. Forgetting alt altogether, on the other hand, can cause a screen reader to read out the whole file name instead, which is worse.

The rule of thumb: if the image adds real information, describe it. If it’s pure decoration, use alt="". Never leave alt out completely.

width and height: not just for sizing

<img src="mountain.jpg" alt="A snow-covered mountain peak" width="600" height="400">

Beginners assume width/height are only for controlling the size on screen. They actually solve a more important problem: layout shift.

When a browser reads your HTML, it hasn’t downloaded the image yet — it doesn’t know how tall or wide it is. Without width/height, the browser first renders the page with no space reserved for the image, then suddenly shifts everything below it down once the image finishes loading. On a slow connection, you’ve probably experienced this yourself: you start tapping a link, and the page jumps at the last second because an image above finally loaded.

Setting width and height (in pixels, matching the image’s real proportions) tells the browser to reserve the correct space immediately, so the page layout stays stable while the image loads.

<!-- The image is naturally 1200x800px. Keep the same ratio: -->
<img src="mountain.jpg" alt="A snow-covered mountain peak" width="600" height="400">

Keep the aspect ratio. If the real image is 1200×800, and you set width="600", set height="400" to match (both halved). Setting a width and height that don’t match the original ratio will stretch or squash the image. If you only want to control the display size, prefer setting just one dimension in CSS (width: 100%; height: auto;) and use the HTML width/height attributes to describe the image’s real size, for layout stability.

Lazy loading: loading="lazy"

<img src="mountain.jpg" alt="A snow-covered mountain peak" loading="lazy">

By default, the browser downloads an image as soon as it discovers the tag (loading="eager", the default value). For a long page with many images — a product listing, a photo gallery — that means downloading images the visitor may never even scroll down to see, slowing down the whole page.

loading="lazy" tells the browser to wait until the image is about to enter the visible screen area before downloading it.

<img src="hero-banner.jpg" alt="Team celebrating product launch">
<!-- ^ No loading="lazy" here — this is above the fold, load it immediately -->

<img src="gallery-photo-12.jpg" alt="Conference attendees networking" loading="lazy">
<!-- ^ Far down the page — lazy load this one -->

Rule of thumb: don’t lazy-load the first image(s) a visitor sees when the page opens (this is sometimes called the image “above the fold”) — you want those to appear as fast as possible. Do lazy-load images further down a long page.

Captioned images: <figure> and <figcaption>

When an image needs a caption — a photo credit, a short explanation — don’t just place a <p> next to it. HTML has a purpose-built pair for this:

<figure>
  <img src="mountain.jpg" alt="A snow-covered mountain peak at sunset" width="600" height="400">
  <figcaption>Mount Kanchenjunga at sunset, photographed from Darjeeling.</figcaption>
</figure>
  • <figure> groups the image (or any self-contained media, like a code snippet or chart) with its caption, and marks the whole thing as one unit that could, in theory, be moved elsewhere in the document without breaking the surrounding text’s meaning — like a figure in a textbook.
  • <figcaption> is the caption itself. It can go before or after the content inside <figure>, and there can be only one per <figure>.

This is more meaningful than a plain <img> followed by a <p>, because <figcaption> is programmatically tied to the image — a screen reader announces it as the image’s caption, not as an unrelated paragraph.

Image formats: which one to use

Different formats exist for different reasons, and picking the right one affects both quality and file size (which affects how fast your page loads):

Format Best for Notes
JPG / JPEG Photos, gradients, complex colour Smaller file size, but loses a little quality every time it’s re-saved. No transparency.
PNG Graphics needing transparency, sharp edges (logos, icons, screenshots) Larger file size than JPG for photos, but no quality loss and supports a transparent background.
SVG Icons, logos, simple illustrations Vector-based — it’s actual code describing shapes, not a grid of pixels, so it stays perfectly sharp at any size and is often tiny in file size.
WebP Almost anything — a modern all-rounder Usually noticeably smaller than an equivalent JPG or PNG at the same visual quality. Supported by all current browsers.

Rule of thumb: photos → JPG or WebP. Logos, icons, anything needing a transparent background or crisp edges at any zoom level → SVG (or PNG if SVG isn’t available). When in doubt today, WebP is a safe modern default for photos.

Serving the right size: srcset and sizes

So far, one <img> has pointed at exactly one file. But think about who’s loading your page: someone on a small phone screen and someone on a huge desktop monitor do not need the same image size. Sending a phone the same giant 2400px-wide photo you’d show on a desktop wastes their data and slows their page down for no visual benefit.

srcset solves this by offering the browser a choice of image files, and letting it pick the best one for the visitor’s actual screen.

<img
  src="photo-800w.jpg"
  srcset="
    photo-400w.jpg 400w,
    photo-800w.jpg 800w,
    photo-1200w.jpg 1200w
  "
  sizes="(max-width: 600px) 100vw, 50vw"
  alt="A calm mountain lake surrounded by pine trees at sunrise"
  width="800"
  height="533"
>

Read it piece by piece:

  • srcset lists several versions of the same image, each followed by its real width written as a number plus w (not “width”, just the letter w) — photo-400w.jpg 400w means “this file is 400 pixels wide.”
  • sizes tells the browser how much space the image will actually take up on the page at different screen widths, so it can work out which file from srcset is the best fit. The example above means: “if the browser window is 600px wide or less, the image will take up the full width of the screen (100vw); otherwise, it will take up half the screen width (50vw).”
  • src stays as a fallback, for any very old browser that doesn’t understand srcset at all.

The browser then does the maths itself — on a narrow phone, it downloads photo-400w.jpg; on a wide desktop window, it may download photo-1200w.jpg. You never have to detect the device yourself; you just hand the browser the choices.

You don’t need srcset on every image. It matters most for large photos that appear at different sizes across devices — a hero banner, a blog’s featured image. A small fixed-size icon or logo does not need it.

Serving a different format with <picture>

srcset offers different sizes of the same format. When you want to offer a modern format (like WebP) with an older format as a fallback, wrap the <img> in a <picture> element:

<picture>
  <source srcset="photo.webp" type="image/webp">
  <source srcset="photo.jpg" type="image/jpeg">
  <img src="photo.jpg" alt="A calm mountain lake surrounded by pine trees at sunrise" width="800" height="533">
</picture>

The browser checks each <source> in order and uses the first format it supports — modern browsers pick the smaller .webp file, while anything that can’t handle WebP falls back to the .jpg. The <img> inside is required: it’s both the final fallback and the element that actually carries alt, width and height.

Putting it together

<article>
  <h2>Our Trek to Everest Base Camp</h2>

  <figure>
    <img
      src="ebc-sunrise.jpg"
      alt="Sunrise casting orange light over snow-capped Himalayan peaks"
      width="800"
      height="533"
      loading="lazy"
    >
    <figcaption>Sunrise at Kala Patthar, Day 8 of the trek.</figcaption>
  </figure>

  <p>The trek took twelve days, starting from Lukla...</p>
</article>

Common mistakes

  • Forgetting alt entirely. Every <img> needs one — a real description, or alt="" for decorative images.
  • Writing lazy, meaningless alt text: "image", "photo", the file name, or just repeating the file’s name like "IMG_4821.jpg".
  • Skipping width/height, causing layout shift as images load — especially noticeable on slower connections.
  • Setting width/height that don’t match the image’s real aspect ratio, stretching or squashing it.
  • Lazy-loading the very first image on the page. This can actually slow down what the visitor sees first, which defeats the purpose.
  • Writing <img></img>, trying to give a void element a closing tag.
  • Using a <p> next to an image instead of <figure>/<figcaption> when the text is genuinely a caption for that image.

Interview-style questions

Why is alt important on every image? It provides a text alternative for screen reader users, shows when the image fails to load, and helps search engines understand the image — it should never be skipped.

When should alt be left empty (alt="")? For purely decorative images that add no real information, so screen readers skip them instead of reading a meaningless description.

Why should you set width and height on an image even if you also size it with CSS? They let the browser reserve the correct space before the image downloads, preventing the page layout from jumping around as images load in — this is called layout shift.

What does loading="lazy" do, and when should you avoid it? It defers downloading an image until it’s about to scroll into view, saving bandwidth on long pages. Avoid it on images visible immediately when the page loads, since delaying those can slow down what the visitor sees first.

What problem does srcset solve? It lets the browser choose from several sizes of the same image and download only the one that fits the visitor’s actual screen, instead of always sending one large file to every device.

Practice

  1. Use the Practice in Editor button. Replace the alt text with your own description of a different image, then try breaking the src (misspell the URL) to see your alt text appear in its place.

  2. Add loading="lazy" to an image, and explain in one sentence when you would and wouldn’t use it.

  3. Find an image online, note its real width and height (right-click → Inspect, or the image properties), and write an <img> tag with matching width/height attributes and a meaningful alt.

  4. Write a srcset with three widths (400w, 800w, 1600w) for one image, and a sizes value that gives the image the full screen width below 500px and half the screen width above it.

  5. Fix this code (it has three mistakes: missing alt, a closing tag on a void element, and width/height that don’t match a 1200×800 original):

    <img src="beach.jpg" width="300" height="150"></img>

Recap

  • <img> is a void element; it needs src (image path) and always needs alt (a real description, or alt="" for decorative images).
  • Good alt text describes what the image shows, briefly, without “image of” or a meaningless file name.
  • width and height reserve space before the image loads, preventing layout shift — keep them matching the image’s real aspect ratio.
  • loading="lazy" defers loading images further down the page, but should be skipped for images visible immediately.
  • <figure> and <figcaption> group an image with a real caption, meaningfully tied together for accessibility.
  • Choose formats by purpose: JPG/WebP for photos, SVG for icons and logos, PNG when you need transparency without SVG.
  • srcset + sizes let the browser pick the right image size for each visitor’s screen; <picture> lets you offer a modern format like WebP with an older fallback.