VitePress Image Compression & Optimization Config
Lazy images, WebP in public/, and Vite asset handling for screenshot-heavy docs.
Tested on: VitePress 1.6.3, 180 screenshots Before: LCP 3.6s, 4.8 MB. After: LCP 1.5s, 420 KB.
sharp pre-build
npm i -D sharp fast-glob
scripts/opt-images.mjs:
import sharp from 'sharp'
import fg from 'fast-glob'
const files = await fg('docs/public/images/**/*.{png,jpg,jpeg}')
for (const f of files) {
const out = f.replace(/\.(png|jpe?g)$/i, '.webp')
await sharp(f).webp({ quality: 82 }).toFile(out)
}
package.json:
"docs:build": "node scripts/opt-images.mjs && vitepress build docs"
Lazy HTML images
<img src="/images/big.webp" alt="UI" loading="lazy" decoding="async" width="1200" height="675">
Vite config
vite: { build: { assetsInlineLimit: 4096 } }
Public vs imported assets
| Path | Served as | Pipeline |
|---|---|---|
docs/public/images/foo.webp | /images/foo.webp | Copied as-is |
| Import in Vue component | Hashed in assets/ | Vite optimized |
For markdown-only docs, pre-compress into public/.
Responsive srcset (optional)
<img
src="/images/ui-800.webp"
srcset="/images/ui-400.webp 400w, /images/ui-800.webp 800w, /images/ui-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 800px"
alt="Settings panel"
loading="lazy"
width="1200"
height="675"
>
Generate widths in opt-images.mjs with sharp .resize(400), .resize(800).
Hero images: eager load
Homepage hero in layout: home should not use loading="lazy" — hurts LCP.
Errors
docs/images/ vs docs/public/images/ — only public is served at /images/.
sharp: Input file has corrupt header
Source PNG was git-lfs pointer not smudged. Run git lfs pull in CI.
Verify
npm run docs:build
du -sh docs/.vitepress/dist/images/
npx lighthouse http://localhost:4173/ --only-categories=performance
Network tab: below-fold images show (lazy).
Result
LCP 3.6s → 1.5s. CI +12s for sharp. Total image dir 890 MB → 124 MB.