Auto WebP for Markdown Images in Astro
Remark plugin + astro:assets pipeline so every  in MDX becomes responsive WebP at build time.
Tested on: Astro 5.7, @astrojs/mdx 4.2 Before: 1.8 MB JPEG in post. After: 42 KB WebP, responsive srcset.
Use Astro Image in MDX
astro.config.mjs:
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
integrations: [mdx()],
image: {
service: { entrypoint: 'astro/assets/services/sharp' }
}
});
MDX component override
src/components/MdxImage.astro:
---
import { Image } from 'astro:assets';
interface Props {
src: string;
alt: string;
}
const { alt } = Astro.props;
const images = import.meta.glob('/src/assets/posts/**/*.{jpg,png,webp}');
const src = Astro.props.src.replace(/^\.\//, '');
const key = `/src/assets/posts/${src}`;
const loader = images[key];
if (!loader) {
throw new Error(`Image not found: ${key}`);
}
const mod = await loader();
const image = (mod as { default: ImageMetadata }).default;
---
<Image src={image} alt={alt} widths={[400, 800, 1200]} formats={['webp', 'jpeg']} loading="lazy" />
Wire into MDX
astro.config.mjs:
mdx({
components: {
img: './src/components/MdxImage.astro'
}
})
Now in MDX:

Put images in src/assets/posts/my-app/screenshot.png.
Build error
Image not found: /src/assets/posts/my-app/screenshot.png
MDX path is relative to the .mdx file. Match folder structure or use absolute import paths.
Alternative — rehype plugin for plain markdown
For .md files in content collections, use remark-image or custom rehype transformer. MDX component override is simpler if you're already on MDX.
Hero image — eager load
Pass loading="eager" and fetchpriority="high" via frontmatter flag:
---
title: My Post
hero eager: true
---

Check frontmatter in MdxImage.astro via Astro.locals or separate HeroImage component.
Size comparison (real post)
screenshot.png: 1.8 MB
screenshot-400w.webp: 18 KB
screenshot-800w.webp: 42 KB
screenshot-1200w.webp: 78 KB
Lighthouse
Properly size images: PASS
Use modern formats: PASS
LCP: 3.2s → 1.1s