Snippet: Astro Image component với AVIF + WebP fallback

Code snippet ngắn để tạo wrapper component cho Astro Image - tự động generate AVIF, WebP và JPG fallback, lazy-load + responsive sizes.

Code astro performance image

Vấn đề

<Image /> mặc định của Astro chỉ output 1 format. Để có AVIF (best) → WebP (fallback) → JPG (last resort) cần wrap thêm <picture> thủ công - lặp lại nhiều lần là code rác.

Snippet bên dưới gói gọn pattern đó thành 1 component dùng được khắp project.

File: src/components/SmartImage.astro

---
import { getImage } from 'astro:assets';
import type { ImageMetadata } from 'astro';

interface Props {
  src: ImageMetadata;
  alt: string;
  widths?: number[];
  sizes?: string;
  loading?: 'lazy' | 'eager';
  class?: string;
}

const {
  src,
  alt,
  widths = [400, 800, 1200, 1600],
  sizes = '(min-width: 1024px) 1200px, 100vw',
  loading = 'lazy',
  class: className,
} = Astro.props;

async function setOf(format: 'avif' | 'webp' | 'jpg') {
  const imgs = await Promise.all(
    widths.map((w) => getImage({ src, format, width: w })),
  );
  return imgs.map((i, idx) => `${i.src} ${widths[idx]}w`).join(', ');
}

const avif = await setOf('avif');
const webp = await setOf('webp');
const jpg  = await setOf('jpg');
const fallback = await getImage({ src, format: 'jpg', width: widths[0] });
---

<picture class={className}>
  <source type="image/avif" srcset={avif} sizes={sizes} />
  <source type="image/webp" srcset={webp} sizes={sizes} />
  <source type="image/jpeg" srcset={jpg}  sizes={sizes} />
  <img
    src={fallback.src}
    alt={alt}
    loading={loading}
    decoding="async"
    width={fallback.attributes.width}
    height={fallback.attributes.height}
  />
</picture>

Cách dùng

---
import SmartImage from '../components/SmartImage.astro';
import hero from '../assets/hero.jpg';
---

<SmartImage
  src={hero}
  alt="Hero banner"
  widths={[640, 1024, 1600, 2400]}
  sizes="(min-width: 1280px) 1280px, 100vw"
  loading="eager"
/>

Kết quả đo được trên natecue.com

  • LCP image: ~140KB (JPG) → ~38KB (AVIF) - giảm ~73%.
  • CLS = 0 do width/height được Astro auto-fill từ ImageMetadata.
  • Lighthouse Performance score: 92 → 98.

Lưu ý

  • AVIF encode chậm hơn WebP nhiều lần - chỉ chạy astro build khi deploy, không cần trên dev.
  • Với SVG/GIF: bypass component này, dùng <img> trực tiếp.
  • Nếu deploy trên Vercel/Netlify: nhớ enable cache cho _astro/* (immutable, 1 năm).

Bài viết liên quan

✦ Miễn phí

Thích bài này? Nhận thêm mỗi tuần

AI workflows, marketing tips, và free tools. Không spam.

Cùng 1,200+ người đang đọc.

Không spam. Unsubscribe bất cứ lúc nào.