#explain image densities

20 messages · Page 1 of 1 (latest)

maiden field
#

how does image densities work? what happens when we define [1,2] as image densities?

small loomBOT
#

Hi @maiden field. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Image Densities in Astro

The densities prop on the <Image /> component allows you to generate multiple versions of an image for different pixel densities (e.g., for high-DPI/Retina displays). [densities prop]

How it works

When you provide a densities array, Astro uses those values to generate a srcset attribute with x descriptors, so the browser can pick the best image version based on the device's screen density.

Example with [1.5, 2]

---
import { Image } from 'astro:assets';
import myImage from '../assets/my_image.png';
---
<Image
  src={myImage}
  width={myImage.width / 2}
  densities={[1.5, 2]}
  alt="A description of my image."
/>

This produces:

<img
  src="/_astro/my_image.hash.webp"
  srcset="
    /_astro/my_image.hash.webp 1.5x
    /_astro/my_image.hash.webp 2x
  "
  alt="A description of my image."
  width="800"
  height="450"
  loading="lazy"
  decoding="async"
/>

So if you define [1, 2], Astro would generate two image variants — one for 1x (standard displays) and one for 2x (Retina/high-DPI displays) — and include them in the srcset. [densities prop]

Important constraints

#
  • Do not use densities together with widths — only one can be used at a time to generate a srcset, since they produce different descriptor types (x vs w). [incompatible options]
  • densities is incompatible with the layout prop (or image.layout config) — it will be ignored if a layout is set. [densities prop]
  • Densities that would result in sizes larger than the original image are ignored to prevent upscaling. [densities prop]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@maiden field Kapa.ai is still learning and improving, please let me know how I did by reacting below.

maiden field
#

@small loom why is the width set to 1/2 in the example?

small loomBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources don't explicitly explain why myImage.width / 2 is used in that example, but the reasoning can be inferred from how densities work:

When you specify densities={[1.5, 2]}, you're telling the browser that the provided image variants are for 1.5x and 2x displays. The width prop defines the base (1x) display size of the image. [densities prop]

So if the original image is 1600px wide, setting width={myImage.width / 2} (i.e., 800px) means:

  • The image is intended to display at 800px on a standard 1x screen.
  • At 1.5x density, the browser will use a version that is 800 × 1.5 = 1200px wide.
  • At 2x density, the browser will use a version that is 800 × 2 = 1600px wide — which matches the original image's full resolution.

This way, the original 1600px image is used efficiently as the high-density variant, without needing to upscale. Densities that would result in sizes larger than the original image are ignored to prevent upscaling. [densities prop]

In short, halving the width is a deliberate choice to make the original image serve as the 2x variant, keeping everything within the bounds of the source image's actual dimensions.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@maiden field Kapa.ai is still learning and improving, please let me know how I did by reacting below.

maiden field
#

@small loom can i choose NOT to optimize an <Image/> element? or should i just use a plain img tag?

small loomBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, you have both options available:

Using <Image /> without optimization

You can still use the <Image /> component for images that won't be optimized — specifically images in the public/ folder or remote images that are not in your authorized domains list. In these cases, the component behaves like a plain <img> tag (no processing), but you still get benefits like CLS prevention: [Astro components]

"The resulting image will be the same as using the HTML <img>. However, using the image component for all images provides a consistent authoring experience and prevents Cumulative Layout Shift (CLS) even for your unoptimized images."

Using a plain <img> tag

You can also use a native HTML <img> tag directly in .astro files. Images rendered this way will not be processed, optimized, or transformed — they are copied into the build as-is. [unprocessed images]

The docs recommend using <img> when:

  • The image format is unsupported
  • You don't want Astro to optimize the image
  • You need to dynamically change the src attribute client-side

Recommendation

Prefer <Image /> even for unoptimized images, since it still prevents CLS by enforcing width and height. Only fall back to <img> when you have a specific reason to need full control over the raw output. [choosing Image vs img]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@maiden field Kapa.ai is still learning and improving, please let me know how I did by reacting below.

maiden field
#

@small loom you failed to tell me how, you tin can.

how do i choose not to optimize with <Image/> ?

small loomBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Fair point — let me be more concrete.

To use <Image /> without optimization, simply point it at an image in your public/ folder or a remote URL that is not in your authorized domains list. Astro cannot process these, so no optimization occurs, but you still get CLS protection. [Astro components]

---
import { Image } from 'astro:assets';
---

<!-- public/ image — not optimized -->
<Image
  src="/images/my-public-image.png"
  alt="descriptive text"
  width="200"
  height="150"
/>

<!-- Remote image outside authorized domains — not optimized -->
<Image
  src="<https://example.com/remote-image.jpg">
  alt="descriptive text"
  width="200"
  height="150"
/>

Alternatively, if you want to globally disable image processing, you can configure the passthrough image service in astro.config.mjs, which disables optimization for all images: [passthrough service]

import { defineConfig, passthroughImageService } from 'astro/config';

export default defineConfig({
  image: {
    service: passthroughImageService()
  }
});

The knowledge sources don't describe a per-image prop on <Image /> to explicitly opt out of optimization for local src/ images — for that use case, a plain <img> tag is the recommended approach. [choosing Image vs img]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@maiden field Kapa.ai is still learning and improving, please let me know how I did by reacting below.

maiden field
#

@small loom well the image i want to NOT optimize is in a remote URL, and that URL is in my domains list. I just want to not optimize it.

small loomBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

That's a tricky situation. The knowledge sources don't describe a way to opt out of optimization for a specific <Image /> component when the image's remote URL is already in your authorized domains list.

Your practical options based on what the docs do say:

  1. Use a plain <img> tag — this bypasses all Astro processing entirely, regardless of whether the domain is authorized: [unprocessed images]

    <img src="<https://example.com/remote-image.jpg"> alt="A description." />
    
  2. Remove the domain from your authorized list — if you remove it from image.domains or image.remotePatterns, Astro will no longer optimize images from that source. You could then use <Image /> and it will pass through unoptimized. However, this would affect all images from that domain, not just one. [authorizing remote images]

The knowledge sources don't mention a per-image prop to disable optimization for an authorized remote image within <Image />. For a single image you want to skip optimization on, using a plain <img> tag is the most straightforward solution.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: