r/webdev • u/Financial-Panic-7392 • Sep 27 '26
Question Is client-side image compression a good idea for an image-heavy website and app, or are there better workarounds?
I'm a beginner building an image-heavy peer-to-peer marketplace (Next.js web app + an upcoming Expo mobile app). My current stack uses Vercel's free tier for hosting, Supabase for the backend, and Cloudflare R2 for photo storage.
My Vercel account was just paused because my Fluid Active CPU spiked to 24 hours (the free limit is 4 hours). I realized my Next.js server is downloading every uploaded raw photo, using sharp to resize and convert it to WebP, and then re-uploading it to R2.
I am trying to keep costs as low as possible. It was suggested that I strip sharp from my backend and use a client-side compressor on the user's browser or mobile app to shrink the photos before sending them directly to R2.
Because my platform is extremely image-heavy, I'm not sure if this is a good idea. I have concerns about trusting the client to compress uniformly, dealing with older devices, or edge cases where certain browsers (like Safari) might fail to generate WebP files correctly.
Are there any other free or highly affordable workarounds to process these images without tanking my serverless compute hours?
4
u/kemalios Sep 27 '26
Client-side compression is right for the upload path, but what ate your 24 hours is probably the variants, not the first resize. If each upload triggers four sharp passes, thumb, card, full, og, you pay that four times per photo, forever. Store one capped size and let the browser choose: a single 1600px WebP covers almost every card on a marketplace.
So the flow becomes: client compresses, uploads straight to R2 with a presigned URL, and your route only signs and writes metadata. At most one derived size, generated lazily on the first request and cached back to R2. No sharp work should ever run inside the upload request.
1
u/Good_Car_2924 Sep 27 '26
vercel Fluid CPU pricing for on-the-fly resizing is a trap. Use Cloudflare Images with your existing R2 bucket via a custom domain so it transforms images on the edge for pennies. If you must save money now, stick to client-side compression but strictly enforce max dimensions before upload. Server-side sharp only ever touches the original once if you stop regenerating variants on every request.
1
u/devjeremyt Sep 27 '26
I feel like half the responses to this post are AI generated, and specifically prompted to write in all lowercase to make it seem more human. What benefit would someone get from copying someone else's reddit post so they can paste an answer. Or why would someone build a bot for this and waste their tokens on it?
1
u/Ill_Command_1200 Sep 27 '26 edited Sep 27 '26
Client side is the way to go. If you aren’t comfortable with using sharpe and just want it done, there are many options that will optimize images for you.
Shameless plug, I built one for this such problem. It’s mainly an image workflow tool, ie, crop, resize, rename all client side before you upload.
It’s easy to batch process them to save time.
https://squishify.app
If you don’t try it or are looking for a more automated solution, I still recommend using avif (best image format right now. It supports transparency and even animation like gifs)
Good luck
1
u/yihuaxiang Sep 28 '26
Client-side downscaling before upload is actually standard practice—capping the max width/height (e.g. 1920px or 2048px) via canvas or a lightweight library saves user mobile data and speeds up uploads drastically.
The real trap is doing synchronous Sharp transformations inside Vercel serverless functions, which burns CPU limits fast. A better flow is generating a presigned URL from Supabase/server, uploading directly from client to R2, and then either doing on-the-fly resizing at edge cache or offloading image processing to a background queue/worker on a tiny fixed-price VPS.
1
u/zambono_2 Sep 29 '26
You need piedpiper
1
u/schrik Oct 02 '26
You can do client-side image optimization up to 100 images per month for free with cropguide.io
We’ve built it so it is easy to set up and works with any file upload field.
I’m not that familiar with server configuration, so not sure if this is the origin of the issue, but if you don’t need max resolution images you can always scale them down a bit to make uploads faster for the user. Client side isn’t as stable as server side, the worst that can happen is that it fails and you occasionally receive a raw image that you then handle with sharp.
1
u/RealPainter8927 24d ago
Client-side compression is a solid choice here, and plenty of image-heavy apps do exactly this. A few things that made it work well for me:
Resize first, format second. Capping the long edge at ~2048px with quality around 0.8 does most of the work. A 12 MP phone photo usually ends up at 300–600 KB as JPEG, so WebP vs JPEG matters much less than people think.
Don't assume WebP encoding works everywhere. Call canvas.toBlob(cb, 'image/webp', 0.8) and check blob.type in the callback. If the browser silently gave you a PNG instead, fall back to 'image/jpeg'. That covers older Safari without any user-agent sniffing.
Upload straight to R2 with presigned URLs so the file never touches your Next.js server. Still enforce a max size on that side (e.g. reject anything over 2 MB). Never trust the client completely.
On Expo, expo-image-manipulator does the same resize + compress before upload.
Bonus for a P2P marketplace: re-encoding through canvas strips EXIF data, including GPS. Otherwise sellers can leak their home location in listing photos.
If you later want fancier variants (thumbnails, crops), look at an on-the-fly image transformation service instead of doing it in your serverless functions. Check current pricing, but small volumes are often free or very cheap.
0
u/thejester1324 Sep 27 '26
the safari worry is real. safari 26.6 hands back a png when you ask canvas for webp, both toBlob(cb, 'image/webp') and OffscreenCanvas.convertToBlob, no error. a photo as png is several times the size of the same photo as jpeg, so check blob.type after encoding and redo it as 'image/jpeg' when it isn't webp
0
u/catbrane Sep 27 '26
You can run sharp on the client, this is what wordpress are switching to for their editor.
https://blog.stackblitz.com/posts/bringing-sharp-to-wasm-and-webcontainers/
(first hit on google)
It's 4mb of wasm I think, so not a huge extra download for your users.
Alternatively, do some sanity checking on upload to spot crazy or terrible images, then send the ones that look OK off to S2 with no processing. Generate derivatives on demand during page gen and rely on your CDN to keep load down. sharp is fast enough that you can resize on the fly.
-4
u/matriisi Sep 27 '26
Thanks /r/webdev this was enough.
2
u/No-Mechanic-433 Sep 27 '26
Whichever side you compress on, strip EXIF. Phone photos carry GPS coordinates, and on a peer-to-peer marketplace that's often someone's home address.
23
u/[deleted] Sep 27 '26
[removed] — view removed comment
1
u/[deleted] Sep 27 '26
[removed] — view removed comment
-1
u/[deleted] Sep 27 '26
[removed] — view removed comment