How to optimize images in your web app without losing quality
Pick the right format, serve images at the size they're shown, load the hero first and the rest later, and handle user uploads so they don't slow your app.
Leer en español →
To optimize images in a web app, serve each image at roughly the size it's displayed, in a modern format like WebP or AVIF, with its width and height declared, loading the main image immediately and the rest only when they're about to scroll into view. Do the same for images your users upload: resize them once, on upload or through an image service, instead of sending the original photo to every visitor. Those few changes usually cut page weight dramatically and improve loading scores with no visible loss of quality.
This guide covers how to spot oversized images, which format to use when, the HTML attributes that matter, what to do about user uploads, and how to check the result.
How do I know my images are the problem?
Open your page in Chrome, then DevTools, then the Network tab, filter by "Img" and reload. Sort by size. Warning signs:
- Single images over a few hundred kilobytes.
- Images whose natural size (hover the file in the Elements panel) is several times larger than the space they occupy on screen.
- PNG files used for photographs.
- Dozens of images loading on a page where only three are visible.
Lighthouse and PageSpeed Insights also flag oversized images and estimate how much you'd save. In AI-built apps the most common case is a hero or product photo uploaded straight from a phone camera, thousands of pixels wide, displayed in a card 400 pixels wide.
Which image format should I use?
| Format | Use for | Notes |
|---|---|---|
| AVIF | Photos and complex images | Usually the smallest files; supported in current major browsers |
| WebP | Photos, and graphics with transparency | Very broad support, a safe default |
| JPEG | Photos, as a fallback | Universal |
| PNG | Screenshots, graphics with sharp edges, transparency | Large for photos |
| SVG | Logos, icons, simple illustrations | Scales to any size; tiny when simple |
For photographs, WebP or AVIF at a quality setting around 75 to 80 is visually indistinguishable from the original for most people. For icons, use SVG instead of PNG files at several sizes.
How should I write the image tag?
A well-formed image in plain HTML or JSX looks like this:
<img
src="/img/team-800.webp"
srcset="/img/team-400.webp 400w, /img/team-800.webp 800w, /img/team-1600.webp 1600w"
sizes="(max-width: 600px) 100vw, 600px"
width="800" height="533"
loading="lazy" decoding="async"
alt="Two people reviewing a printed floor plan">
What each part does:
srcsetandsizeslet the browser pick the smallest file that still looks sharp on that screen, including high-density phone screens.widthandheightreserve the space before the file arrives, so the layout doesn't jump. They describe the aspect ratio; CSS can still make the image responsive.loading="lazy"delays offscreen images until the user scrolls near them.altdescribes the image for screen readers and search engines. Leave it empty (alt="") for purely decorative images.
The one exception: your main image
The large image at the top of the page is often the Largest Contentful Paint element, the moment Google uses to judge loading. For that image, do the opposite: never lazy-load it, add fetchpriority="high", and make sure it appears in the initial HTML rather than being injected later by JavaScript or set as a CSS background. Our guide to Core Web Vitals for your app explains why.
What about images my users upload?
User uploads are where most of the weight comes from, because you don't control them. A sensible setup:
- Resize before or right after upload. Either shrink the image in the browser before sending it (a canvas resize to, say, 2000 pixels on the long side) or process it on the server.
- Strip metadata. Photos from phones can contain EXIF data, including the GPS location where they were taken. Re-encoding the image usually removes it; check that yours does, because publishing a user's home location by accident is a privacy problem.
- Store one original, serve variants. Generate a thumbnail and a medium size, or use an image transformation service that resizes on request and caches the result.
- Limit what you accept. Set a maximum file size and allowed types in the upload form and enforce them on the server too.
If you store files in Supabase Storage, its image transformations (available on the Pro plan and above) let you request a resized version by adding width, height and quality parameters, and it serves WebP automatically to browsers that support it. Transformed images count against a monthly quota of origin images, so check the current limits on your plan. If you're on Vercel with Next.js, the built-in next/image component and Vercel's image optimisation handle resizing and formats in a similar way, also with usage limits. For how uploads work in a Lovable app, see uploading files to Supabase Storage.
Where do I put static images?
Images that ship with your app (logos, illustrations, marketing photos) should be compressed once at build time and served from a CDN with long cache headers. Most hosts for Vite and Next.js apps do the CDN part automatically. Give processed files a content hash in their name, or a version in the path, so you can cache them for a long time and still replace them.
How do I check it worked?
- Reload with the Network tab open and compare the total transferred size against before.
- Throttle the connection to "Fast 4G" or "Slow 4G" in DevTools and watch how the page fills in.
- Rerun Lighthouse and look at the image-related audits and the LCP time.
- Look at the images on a real phone and on a large monitor. If anything looks soft, raise the quality a little or add a larger size to
srcset.
Quick answers
WebP or AVIF? Either is a big improvement over JPEG and PNG. AVIF is often smaller; WebP encodes faster and is the safer default if you have to pick one.
Does lazy loading hurt SEO? Native loading="lazy" is fine for images below the first screen. Don't lazy-load the main image, and don't hide images behind scroll events that crawlers never fire.
How big should a hero image be? Wide enough for the largest screen you care about at its display size, typically under 2000 pixels, provided as several sizes through srcset.
Can my AI tool do this? It can add attributes and convert static files if you ask precisely. Upload processing and image services usually need hands-on work in the code and storage settings.
Want someone to tidy this up?
Image work is quick to describe and tedious to apply everywhere. Our maintenance and support plans include this kind of performance work, and custom software development covers building a proper upload and resize pipeline. Run the free health check for an outside view, or send us a message. The web.dev guide to optimising LCP goes deeper on loading the main image.


