Astro Image Component and Optimization
Master Astro's built-in Image and Picture components to ship responsive, lazy-loaded, modern-format images without external services.
What you'll learn
- ✓How Astro processes local and remote images
- ✓The difference between Image and Picture components
- ✓How to ship AVIF and WebP automatically
- ✓Lazy loading and layout shift prevention
- ✓When to use a CDN instead
Prerequisites
- •HTML basics
- •An Astro project
What and Why
Images are usually the largest payload on a page. Astro ships an <Image /> and <Picture /> component from astro:assets that handle resizing, format conversion, and lazy loading at build time, without an external service. The result: smaller files, modern formats served to capable browsers, and explicit dimensions that prevent layout shift.
Why does this matter? Because Core Web Vitals reward sites that load fast and stay stable. A 2 MB hero image kills LCP. The same image in AVIF, served at the exact size needed, can drop under 100 KB with no visual loss.
Mental Model
Think of Astro’s image pipeline as a build-time darkroom. You hand it a high-resolution master image. During the build, it develops multiple prints at different widths and formats. Each <img> tag in your HTML points to the right print, and the browser picks the best match based on viewport and capability.
For local images, this happens at build time. For remote images, you must list the allowed domains and the work happens at request time (in SSR) or build time (in static mode).
Hands-on Example
Place an image at src/assets/hero.jpg and import it:
---
import { Image } from 'astro:assets';
import hero from '../assets/hero.jpg';
---
<Image
src={hero}
alt="Mountains at sunset"
width={1200}
height={600}
format="avif"
loading="lazy"
/>
Astro reads the original file, resizes it to 1200x600, converts to AVIF, and emits the optimized file into your dist/_astro folder. The generated HTML includes width and height attributes so the browser reserves space before the bytes arrive.
For multiple formats with a fallback, use <Picture />:
---
import { Picture } from 'astro:assets';
import hero from '../assets/hero.jpg';
---
<Picture
src={hero}
alt="Mountains at sunset"
widths={[400, 800, 1200]}
formats={['avif', 'webp']}
sizes="(max-width: 768px) 100vw, 1200px"
/>
This emits a <picture> element with multiple <source> tags. The browser picks the smallest viable file.
src/assets/hero.jpg (3 MB original)
|
v
+---------------------+
| Astro asset |
| pipeline (sharp) |
+---------------------+
| | |
v v v
400w 800w 1200w
| | |
+-----+-----+
|
avif + webp
|
v
<picture> tag
with srcset
|
v
browser picks
best variant For remote images, configure allowed domains in astro.config.mjs:
export default defineConfig({
image: {
domains: ['images.unsplash.com'],
},
});
Common Pitfalls
- Importing from the public folder. Files in
public/are copied as-is and skipped by the optimizer. Put images insrc/to get processing. - Missing width or height. Without dimensions, you cause cumulative layout shift. Astro warns but still emits.
- Forgetting alt text. Astro requires
alt. Use an empty string for purely decorative images. - Over-optimizing. Generating dozens of widths bloats your build output and hosting bill. Stick to 3 to 5 sensible breakpoints.
- Remote images without domain allowlist. They fail loudly in production.
Best Practices
- Always import images into the page; never reference them as strings.
- Set
loading="lazy"for below-the-fold images andloading="eager"for the hero. - Use
<Picture />with both AVIF and WebP for the best compression and compatibility. - Provide a meaningful
sizesattribute so the browser picks correctly. - Run a Lighthouse audit after launch to confirm the optimizations actually shipped.
- For huge libraries of user-uploaded images, consider a CDN like Cloudinary; Astro is best for content you ship with the site.
Wrap-up
Astro’s image components turn one of the messiest parts of web performance into a tidy declarative API. You import an image, set a few attributes, and the build produces modern formats, responsive variants, and stable layouts. For most projects, you can delete your image CDN bill and let the static pipeline handle it. Combine <Picture />, explicit dimensions, and lazy loading, and your Lighthouse scores will reflect the effort almost immediately.
Related articles
- Astro Astro Islands Architecture Explained
Learn how Astro ships zero JavaScript by default and only hydrates the interactive components you mark as islands.
- Next.js Next.js Image Optimization Deep Dive
Learn how the next/image component handles responsive sizing, lazy loading, format conversion, and caching to ship faster sites.
- Astro Astro Content Collections Tutorial
Build a typed blog with Astro content collections, including Zod schemas, references, and dynamic routes generated from Markdown files.
- Astro Astro Integrations and Adapters: An Overview
Understand the difference between Astro integrations and adapters, when to reach for each, and how they shape your deployment pipeline.