Skip to content

Images and assets

The rule: smallest bytes that still look right at the target size. Everything else follows from that.

  • Directorysrc/
    • Directoryassets/ # ← put source images here (Astro optimizes)
      • Directoryheroes/ # ← 1200×480 hero images
      • Directorydemo/ # ← inline diagrams / illustrations
  • Directorypublic/ # ← raw, unprocessed (favicon, robots.txt, OG)
  • Directorysrc/content/docs/…/ # ← markdown next to co-located images

Which format?

  • SVG — icons, diagrams, logos with < ~200 shapes. Vector, scales for free, gzips well.
  • AVIF — photos, best compression (25–50% smaller than WebP). Slightly less browser support pre-2020.
  • WebP — photos, universal support today. Astro emits these by default.
  • PNG — pixel-perfect UI screenshots. Larger files; use sparingly.
  • JPG — legacy, rarely the right pick anymore.
post.mdx
---
title: A post with an image
---
import { Image } from 'astro:assets';
import hero from '~/assets/heroes/kubernetes.svg';
# My post
<Image
src={hero}
alt="Kubernetes hero — hexagonal wheel graphic on dark navy"
width={1200}
height={480}
loading="lazy"
decoding="async"
/>

What Astro does automatically:

  • Hashes the filename (kubernetes.CzR3v6.svg) — infinite cache.
  • Picks a format based on browser support (raster only — SVG passes through).
  • Emits width / height on the tag to prevent CLS.
  • Generates responsive srcset if you pass a widths prop.

The hero on this page is a hand-tuned SVG. It’s 1.2 KB gzipped vs typically 40–120 KB for a WebP hero at 1200 px wide.

Kubernetes hero — hexagonal wheel graphic on dark navy

For real photos, use <Image> with widths and sizes to serve exactly what the viewport needs:

responsive.mdx
<Image
src={houston}
alt="Astro's mascot, Houston"
widths={[320, 640, 960]}
sizes="(max-width: 640px) 320px, (max-width: 1024px) 640px, 960px"
loading="lazy"
/>
Astro's mascot, Houston, on a dark background

Astro emits three variants and lets the browser pick the smallest that matches the layout.

<Picture /> (also from astro:assets) emits a <picture> with format fallbacks. Use it when you want AVIF for browsers that support it, WebP for the rest, PNG as a floor:

picture.mdx
import { Picture } from 'astro:assets';
<Picture
src={houston}
formats={['avif', 'webp']}
fallbackFormat="png"
alt="Houston"
widths={[320, 640]}
sizes="(max-width: 640px) 320px, 640px"
/>
Houston, with AVIF/WebP/PNG fallbacks

For infra diagrams, hand-authored SVGs beat every raster format. This one is 1.9 KB uncompressed:

Simple 3-tier architecture: users → ingress → services → pods → RDS

Plain markdown images still work — Astro processes them:

![Houston](../../assets/houston.webp)

They render as <img> without the srcset niceties. Prefer the <Image> component in .mdx.

PropPurpose
altRequired. Screen-reader description.
loading"lazy" for below the fold, "eager" for above.
decoding"async" for everything (default).
widthsArray — responsive srcset.
sizesCSS-length hint per media query — pairs with widths.
quality1–100 for lossy formats. Default 80.
formatForce a format (avif, webp, png, jpg).
densities[1, 2] for retina.

Target ≤ 50 KB transferred. Prefer SVG. If it must be raster, AVIF at Q60 + loading="eager".