In modern web development, managing images is more complex than it seems. It’s not enough to just put an <img> tag.
You need to convert to modern formats (WebP/AVIF), generate different sizes for mobile and desktop (srcset), and reserve space to avoid content layout shifts (CLS).
Doing this manually is,… I’ll say tedious (to maintain elegance). Fortunately, Astro does it for us.
But before seeing the component for it, we must resolve the existential doubt of every Astro developer: Where do I save my files?
/public folder or /src/assets folder?
In your project structure, you’ll see two places where it seems logical to save images. Understanding the difference is very important.
The /public folder
It’s like a “catch-all drawer”. The files you put here Astro does not touch them. They are copied as-is to the root of your final website.
- Access path: They are accessed via an absolute URL path (
/my-image.png). - Processing: None. If the image weighs 5MB, the user will download 5MB.
- Cache: They don’t have a hash in the name, so if you change the image but keep the name, users might still see the old cached version.
- For
favicon.ico,robots.txt, orsitemap.xml. - For images you don’t want to be processed (e.g., complex vector graphics or interactive SVGs).
- Images used in standard Markdown files (though this is changing).
The /src/assets folder
This is where the magic happens. Files that live inside src (usually in src/assets) are processed by Astro (and Vite).
- Access path: They must be imported into the JavaScript/Astro code as if they were modules.
- Processing: Astro can analyze, optimize, and resize them.
- Cache: When compiling, Astro renames the file with a unique hash (e.g.,
logo.a4b3c.png), ensuring the user always downloads the latest version.
Try to save all your images in src/assets. Use public only as a last resort.
The <Image /> component
To take advantage of automatic optimization, Astro provides us with the <Image /> component. This component replaces the standard HTML <img> tag.
To use it, we import it from the astro:assets package (included in the core).
Usage with local images
Let’s say we have a photo in src/assets/my-photo.jpg.
---
// We import the component
import { Image } from 'astro:assets';
// We import the image as if it were a JS module
import myPhoto from '../assets/my-photo.jpg';
---
<Image
src={myPhoto}
alt="An accessible description of the photo"
width={800}
/>
When compiling, this will not generate a normal <img> tag pointing to the original JPG. Astro will do the following:
- Format conversion: It will automatically convert the image to WebP (or AVIF), which weighs much less while maintaining quality.
- Dimensions: It will detect the original height and width to add them to the HTML and avoid Cumulative Layout Shift (the page “dancing” when the image loads).
- Lazy Loading: It will add
loading="lazy"anddecoding="async"by default.
The resulting HTML in the browser will be something like this:
<img
src="/_astro/my-photo.hash123.webp"
width="800"
height="600"
loading="lazy"
decoding="async"
alt="An accessible description of the photo"
/>
The <Picture /> component
The <Image /> component covers 90% of cases. But sometimes we need “Art Direction”: to show a square-cropped image on mobile and a wide panoramic one on desktop.
For that, there is the <Picture /> component, which generates an HTML <picture> tag with multiple sources (<source>).
---
import { Picture } from 'astro:assets';
import myImage from '../assets/landscape.jpg';
---
<Picture
src={myImage}
formats={['avif', 'webp']}
widths={[240, 540, 720, myImage.width]}
sizes={`(max-width: 360px) 240px, (max-width: 720px) 540px, (max-width: 1600px) 720px, ${myImage.width}px`}
alt="An amazing landscape"
/>
This will generate multiple versions of the image in different formats and sizes, and the browser will choose the most optimal one based on screen width and browser compatibility.
- Use
<Image />by default. It’s simpler and covers standard optimization. - Use
<Picture />only if you need to change the aspect ratio or the source of the image based on screen size.
Images in Markdown
If you are writing a blog in Markdown, you’ll be happy to know that Astro can also optimize those images.
If you place your images in src/assets and use relative paths in your Markdown:

Astro (through its Markdown integration) will detect that reference, process the image just like the <Image /> component, and generate the optimized tag.
