Image Optimization in SvelteKit Static Sites (No Cloudinary)
Vite imagetools, responsive srcset at build time, and fixing the 'image not found in production' bug.
Tested on: SvelteKit 2.16, vite-imagetools 7.0 Problem: 2.4 MB hero PNG killing mobile Lighthouse score.
Install
npm install -D vite-imagetools
vite.config.ts:
import { sveltekit } from '@sveltejs/kit/vite';
import { imagetools } from 'vite-imagetools';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit(), imagetools()]
});
OptimizedImage component
src/lib/components/OptimizedImage.svelte:
<script lang="ts">
import hero from '$lib/assets/hero.png?w=400;800;1200&format=webp&as=picture';
let { alt = '', class: className = '' } = $props();
</script>
<picture>
{#each hero.sources as source}
<source srcset={source.srcset} type={source.type} />
{/each}
<img
src={hero.img.src}
width={hero.img.w}
height={hero.img.h}
{alt}
class={className}
loading="lazy"
decoding="async"
/>
</picture>
Put source images in src/lib/assets/, not static/.
Production 404 — the classic mistake
GET /_app/immutable/assets/hero.abc123.webp 404
Cause: referenced static/images/hero.png with imagetools query. Imagetools only processes imports from src/.
Fix: move to src/lib/assets/hero.png and import with ?w=...&format=webp.
LCP image — don't lazy load
<img loading="eager" fetchpriority="high" ... />
Only the hero above the fold. Everything else: loading="lazy".
TypeScript module declaration
src/app.d.ts:
declare module '*?w=*&format=webp&as=picture' {
const value: {
sources: { srcset: string; type: string }[];
img: { src: string; w: number; h: number };
};
export default value;
}
Without this, tsc screams on the import query string.
Build size comparison
Original hero.png: 2.4 MB
Generated WebP (400w): 28 KB
Generated WebP (800w): 62 KB
Generated WebP (1200w): 98 KB
Total shipped (browser picks one): ~62 KB avg
Verify in build output
npm run build
find build -name '*.webp' | head
Lighthouse
LCP: 4.1s → 1.5s
Performance score: 71 → 94
Properly size images audit: PASS