After I run npm run build, the images inside my dist/astro folder are only .jpg even though I call getImage with the format of Avif + Webp. Something else I noticed is that the size of my jpg images in my dist folder are the same as in my /src/assets but I thought that after buid, the images would be compressed and the file size would be smaller, am I wrong?
#Optimising Images
33 messages · Page 1 of 1 (latest)
Hi @brazen osprey. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
getImage() and the <Image /> / <Picture /> components can transform images (including format conversion) only when Astro is actually processing those files as part of the astro:assets pipeline. Images that live in public/ or are referenced only as plain <img src="/..."> are not processed at all and will be copied as‑is, with the same format and file size as in your source. [Images guide]
From the docs:
- Local images in
src/are what Astro can “transform, optimize and bundle”. [Images guide] - Files in
/publicare “always served or copied into the build folder as‑is, with no processing.” [Images guide]
Also, the documented default output format for the <Image /> component is .webp unless you override format. [format prop]
The knowledge sources you’ve provided do not include the detailed getImage() API or show how multiple formats like AVIF + WebP are emitted on disk, so I can’t confirm:
- How
getImage({ format: ['avif','webp'] })is supposed to map to actual files indist/ - Whether
getImage()will additionally emit a JPEG fallback or leave the original JPEG untouched
If you are seeing only .jpg files in dist/astro with the same size as in src/assets, that suggests either:
- Those particular files are not going through
astro:assets(e.g. they’re referenced as plain URLs or coming frompublic/), or getImage()is not being used in the way the image pipeline expects (but the docs above don’t provide enough detail to say exactly why).
Given the limitations of these docs, I’d check:
- That your source images are imported from
src/(notpublic/), e.g.import img from '../assets/foo.jpg'. - That you are actually using the result of
getImage()(e.g.<img src={image.src} ...>or via<Image />) instead of the original path. - Whether using the
<Image />component withformat="webp"(or leaving it as default) produces.webpfiles as documented; this would confirm that the pipeline is working. [format prop]
Beyond this, the knowledge sources don’t contain enough information to explain why you specifically only see .jpg outputs or no compression for your getImage() usage.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@brazen osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@outer shuttle My images are indeed coming from src/ not public
import { getImage } from "astro:assets";
interface Props {
desktopURL: string | ImageMetadata;
tabletURL?: string | ImageMetadata;
mobileURL?: string | ImageMetadata;
mobileWidth: number;
mobileHeight: number;
alt: string;
priority: boolean;
overlay?: boolean;
class?: string;
}
const {
desktopURL,
tabletURL,
mobileURL,
mobileWidth,
mobileHeight,
alt,
priority = false,
overlay = true,
class: className,
...rest
} = Astro.props;
// Desktop
const desktopAvif = await getImage({
src: desktopURL,
priority: priority,
format: "avif",
});
const desktopWebp = await getImage({
src: desktopURL,
priority: priority,
format: "webp",
});
const desktopJpg = await getImage({
src: desktopURL,
priority: priority,
format: "jpg",
});```
/// This is part of my CustomPicture component
Let me check the knowledge base for you again. One moment! :books:
Astro’s image pipeline only transforms images that are actually used via astro:assets in your templates. In your snippet you are calling getImage(), but the docs you provided don’t show how those desktopAvif, desktopWebp, desktopJpg values are used afterward, or exactly what Astro writes to dist/ for them. Because of that, I can’t say from the docs why you only see .jpg files or why they’re not smaller.
From the custom image recipe, the expected pattern is: call getImage() and then use the returned .src in your HTML, e.g.:
const mobileImg = await getImage({
src: mobileImgUrl,
format: "webp",
width: 200,
height: 200,
});
<picture>
<source media="(max-width: 799px)" srcset={mobileImg.src} />
<img src={mobileImg.src} alt={alt} />
</picture>
Here, the mobileImg.src URL is what causes Astro to generate the optimized file into dist/astro. [Build custom image]
Given what’s in the docs, the most we can say is:
- Your images are in
src/, which is required for processing. [Build custom image] - To get the transformed assets (Avif/WebP/JPEG) and any compression Astro applies, you must render the
getImage()result URLs in your markup (e.g.src={desktopWebp.src}in<img>/<source>). [Build custom image]
The knowledge sources you shared do not document:
- Whether
format: "jpg"changes compression level vs. your source JPEGs. - How aggressive the compression is, or whether sizes will necessarily be smaller than the originals.
- How multiple format calls for the same source show up in
dist/astro.
So, I can’t confidently explain why your JPEGs in dist/astro are the same size as in src/assets, or why only .jpg files are visible there, based solely on these docs.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@brazen osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.
I cant include the full code, but this should be enough so you can see that I am using the src from getImage @outer shuttle
import { getImage } from "astro:assets";
const {
desktopURL,
tabletURL,
mobileURL,
mobileWidth,
mobileHeight,
alt,
priority = false,
overlay = true,
class: className,
...rest
} = Astro.props;
// Desktop
const desktopAvif = await getImage({
src: desktopURL,
priority: priority,
format: "avif",
});
const desktopWebp = await getImage({
src: desktopURL,
priority: priority,
format: "webp",
});
const desktopJpg = await getImage({
src: desktopURL,
priority: priority,
format: "jpg",
});
---
<picture class:list={[className]} {...rest}>
<!-- Desktop -->
<source media="(min-width: 62.5rem)" srcset={desktopAvif.src} type="image/avif" />
<source media="(min-width: 62.5rem)" srcset={desktopWebp.src} type="image/webp" />
<source media="(min-width: 62.5rem)" srcset={desktopJpg.src} type="image/jpg" />
<!-- Tablet -->
{tabletAvif && <source media="(min-width: 40rem)" srcset={tabletAvif.src} type="image/avif" />}
{tabletWebp && <source media="(min-width: 40rem)" srcset={tabletWebp.src} type="image/webp" />}
{tabletJpg && <source media="(min-width: 40rem)" srcset={tabletJpg.src} type="image/jpg" />}
{mobileAvif && <source media="(max-width: 40rem)" srcset={mobileAvif.src} type="image/avif" />}
{mobileWebp && <source media="(max-width: 40rem)" srcset={mobileWebp.src} type="image/webp" />}
<img
src={mobileJpg ? mobileJpg.src : desktopJpg.src}
alt={alt}
width={mobileJpg?.attributes.width ?? desktopJpg.attributes.width}
height={mobileJpg?.attributes.height ?? desktopJpg.attributes.height}
loading={priority ? "eager" : "lazy"}
decoding={priority ? "sync" : "async"}
fetchpriority={priority ? "high" : "auto"}
/>
</picture>```
Let me check the knowledge base for you again. One moment! :books:
You are using getImage().src correctly; that matches the official “custom image component” recipe. [Build custom image]
However, the behavior you’re observing (only .jpg files in dist/astro, and with similar sizes to the originals) is not described in the docs you provided, so I can’t definitively explain it from documentation alone.
From the available docs, we can say:
getImage()is intended to generate a transformed image for each call, and you then useresult.srcin your<picture>/<img>tags. That’s exactly what you’re doing. [Build custom image]- The recipe examples only show a single
format: "webp"output and don’t describe:- What compression level is used.
- Whether the resulting file is expected to be smaller than your source JPEG.
- What happens when you request
"avif","webp", and"jpg"separately for the same source.
The docs that do talk about multiple formats focus on the <Picture /> component: you pass formats={['avif','webp']} and it emits <source> tags plus a fallback <img>, but they also don’t specify compression ratios or a guarantee that files will be smaller than the originals. [Picture component]
So, based only on the documentation you shared:
- Your usage pattern (multiple
getImage()calls, using*.srcin<source>and<img>) is consistent with the official recipe. - The docs do not state that:
- The generated JPEG must be smaller than the original.
- AVIF/WEBP files must always be emitted in addition to JPEG, or exactly how they will appear on disk.
- Therefore I can’t say from docs alone why you see:
- Only
.jpgfiles indist/astro, or - No noticeable size reduction on those JPEGs.
- Only
At this point, the knowledge sources do not contain enough detail about getImage()’s disk output and compression behavior to explain your specific build result with certainty.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@brazen osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Lit
@outer shuttle Can you give me a full breakdown of widths, sizes and densities please. I dont understand when to use either
Let me check the knowledge base for you again. One moment! :books:
For Astro’s astro:assets <Image /> component, widths, sizes, and densities all control how the srcset is generated, but they are mutually exclusive ways of doing it.
widths
- Type:
number[] | undefined - What it does: Explicit list of pixel widths Astro should generate for this image. [Image widths]
- Effect: Astro generates one file per width and builds a
srcsetlikeimg-240.webp 240w, img-540.webp 540w, .... - Requirements:
- You must also provide a
sizesattribute when usingwidths. [Image widths] - Do not use
densitiesat the same time; only one can controlsrcset. [Image widths] - Any width larger than the original image is ignored (no upscaling). [Image widths]
- You must also provide a
When to use:
You know the exact layout widths you care about (e.g. 240, 540, 720, 1600) and you want full manual control.
Example from the docs: [Image widths]
---
import { Image } from 'astro:assets';
import myImage from '../assets/my_image.png'; // 1600x900
---
<Image
src={myImage}
widths={[240, 540, 720, myImage.width]}
sizes={`(max-width: 360px) 240px, (max-width: 720px) 540px, (max-width: 1600px) 720px, ${myImage.width}px`}
alt="A description of my image."
/>
sizes
- What it is: The standard HTML
sizesattribute passed to<img>. - What it does in Astro: When you give Astro
widths, it usessizesplus those widths so the browser can choose the best image; if you use alayoutprop, Astro can auto-generatewidthsandsizesfor you. [Image widths]
When to use:
- Always with
widths. - Or rely on Astro’s automatic
sizeswhen using alayoutprop (then you usually don’t need to setsizesmanually).
densities
- What it is: An alternative way (not shown in the provided docs) to generate a
srcsetusing device pixel ratios (1x, 2x, 3x) instead of explicit widths. - Relation to
widths: The docs explicitly say you must not providedensitieswhen usingwidths; only one of them can be used to generatesrcset. [Image widths]
When to use (conceptually):
- Pick
densitiesif you want 1x/2x/3x variants of one layout width (e.g. an icon or fixed-size image). - Pick
widthsif you want multiple layout widths across breakpoints.
Most of the time
- If you use a
layoutprop on<Image />, Astro will auto-generatewidthsandsizesfor responsive images, so you usually don’t need to setwidths/sizesmanually. [Image widths] - Only override with
widths+sizeswhen you have specific needs that differ from the automatic behavior.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@brazen osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@outer shuttle Can widths and sizes even be used with getImage() ?
Let me check the knowledge base for you again. One moment! :books:
The documentation you’ve provided only describes widths and sizes as props of the <Image /> component from astro:assets, where they are used to generate the srcset attribute. [Image widths]
There is no mention in these sources of widths or sizes being accepted options for getImage(), nor any example of using them with getImage(). Because of that, I cannot confirm that they are supported with getImage(), or how they would behave if passed.
So, based on the available docs:
widthsandsizesare explicitly documented for<Image />. [Image widths]- The knowledge sources do not state that these options can be used with
getImage(), and they provide no examples of doing so.
I’m therefore not able to say that widths and sizes can be used with getImage() from the documentation you’ve shared.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@brazen osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.
List all the properties that can be used with getImage() @outer shuttle
Let me check the knowledge base for you again. One moment! :books:
The knowledge sources you provided only say that:
getImage()takes an options object with the same properties as the Image component (exceptalt). [API reference]
However, none of the included documents actually list the full set of <Image /> properties, so from these sources alone I cannot reliably enumerate all properties that can be passed to getImage().
All that can be stated with confidence from the docs you shared is:
getImage()accepts an options object.- That options object supports “the same properties as the Image component (except
alt)”. [API reference]
The detailed list of those properties is not present in the provided material, so I’m unable to list them all without going beyond the available documentation.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@brazen osprey Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Stop saying that it takes all the same properties as the Image component except <alt> - It doesnt work like that if widths and sizes dont work on getimage @outer shuttle
Let me check the knowledge base for you again. One moment! :books:
You’re right to call this out, and your reasoning is solid.
From the official, current docs for astro:assets:
getImage()takes an options object with the same properties as the Image component (exceptalt). [astro:assets getImage]
And the same page defines the <Image /> props (including widths and sizes) under “Image properties”. [Image properties]
So, according to the documentation itself:
getImage(options)should accept anoptionsobject typed asUnresolvedImageTransform. [astro:assets getImage]- That
UnresolvedImageTransformis described as “the same properties as the Image component (exceptalt)”, which includes things likewidth,height,format,fit,quality, andwidths/sizes. [Image properties]
If, in practice, some of those props (e.g. widths, sizes) don’t work or don’t behave as expected when passed to getImage(), that’s a mismatch between the implementation and the docs, not something the docs themselves acknowledge or clarify. The knowledge sources do not:
- List any props that are excluded from
getImage()beyondalt. - Call out
widthsorsizesas exceptions. - Provide examples of
getImage()+widths/sizesto confirm the behavior one way or another.
So there are two important takeaways, staying strictly with the docs:
- The documented contract is: “same properties as the Image component (except
alt)”. [astro:assets getImage] - The docs do not explain or qualify that some of those properties might not work with
getImage(); if they don’t in reality, that limitation is not documented in the sources you’ve shared.
Given that, I should:
- Treat “same properties as Image (except
alt)" as the intended API, because that’s what the docs say. - Also be explicit that the docs don’t provide enough detail or examples to reconcile the behavior you’re seeing (e.g.
widths/sizesapparently not working withgetImage()), so I cannot explain that inconsistency from documentation alone.