4.1 KiB
F-055 — Implementer Report
Feature
Image thumbnails: server-rendered cache + CSS max-size
Approach
Originally planned on-demand resize via route handler (?w=40 / ?w=200).
Discovered that Next.js 16 prioritizes static files in public/ over the
app/uploads/[filename]/route.ts handler, making the query-param approach
unworkable. Switched to pre-generating thumbnails to disk under
public/uploads/<width>/ and using static URLs (/uploads/40/foo.jpg,
/uploads/200/foo.jpg) which Next.js serves as plain immutable assets.
Changes Made
1. Thumbnail pre-generation script
New files:
project/scripts/generate-thumbnails.sh— wrapper that locates sharpproject/scripts/generate-thumbnails.mjs— node script using sharp
The script reads source images from apps/admin/public/uploads/ and generates
40px and 200px thumbnails in each app's public/uploads/40/ and
public/uploads/200/ directories. Existing thumbnails are skipped, so
re-runs are idempotent and fast.
2. Sync uploads mirror thumbnails too
File: project/scripts/monolith.sh
sync_uploads() now mirrors find -maxdepth 2 (covers <app>/public/uploads/<size>/<filename>)
to all three apps and calls generate-thumbnails.sh --quiet after syncing.
3. CSS max-height on product detail pages
Files:
project/storefront/src/app/productos/[slug]/page.tsx→max-h-[600px]project/frontend/src/app/products/[slug]/page.tsx→max-h-[500px]to image container
4. UI uses static thumbnail URLs
Files:
project/apps/admin/src/features/products/components/sections/ImagesSection.tsx→img.url.replace('/uploads/', '/uploads/200/')project/apps/admin/src/app/(dashboard)/products/page.tsx→p.imageUrl.replace('/uploads/', '/uploads/40/')
5. Old dynamic route handlers removed
Removed: app/uploads/[filename]/route.ts in admin, frontend, and storefront.
Reason: Next.js 16 prioritizes static files in public/ over route handlers,
so the ?w= approach silently served the original full-size image regardless
of query params. Static URLs /uploads/40/... avoid the collision.
Tradeoff: Newly uploaded images (after next start) won't be served
immediately because Next.js caches the public/ listing at build/start.
This was already the case before this change; thumbnails don't make it worse.
6. Dependencies
sharp was installed in:
project/apps/admin/package.jsonproject/frontend/package.jsonproject/storefront/package.json
Verification
# Generate thumbnails for existing images
./project/scripts/generate-thumbnails.sh
# Original (admin)
curl -sI "http://localhost:3004/uploads/fa7a31db...jpg" | grep -i length
# Content-Length: 129711 (600x800)
# 40px thumbnail
curl -sI "http://localhost:3004/uploads/40/fa7a31db...jpg" | grep -i length
# Content-Length: 1155 (40x53)
# 200px thumbnail
curl -sI "http://localhost:3004/uploads/200/fa7a31db...jpg" | grep -i length
# Content-Length: 12164 (200x267)
# Same in frontend (3003) and storefront (3005): identical sizes
Files Changed
project/scripts/monolith.sh— sync uploads mirrors thumbnailsproject/scripts/generate-thumbnails.sh— newproject/scripts/generate-thumbnails.mjs— newproject/storefront/src/app/productos/[slug]/page.tsx— max-h-[600px]project/frontend/src/app/products/[slug]/page.tsx— max-h-[500px]project/apps/admin/src/app/(dashboard)/products/page.tsx— /uploads/40/project/apps/admin/src/features/products/components/sections/ImagesSection.tsx— /uploads/200/project/frontend/src/app/uploads/[filename]/route.ts— DELETEDproject/storefront/src/app/uploads/[filename]/route.ts— DELETEDproject/apps/admin/src/app/uploads/[filename]/route.ts— DELETED
Note on Subsequent Uploads
After uploading a new image through admin:
- Image is mirrored to frontend/storefront by
api/upload/route.ts - Run
./project/scripts/generate-thumbnails.shto create the thumbnails - Restart the affected app if you want the new image served immediately (otherwise it appears after the next build/start)