Build a Profile / Avatar Upload with Cropping

“Build an avatar upload” is a classic frontend machine-coding question that looks simple until you implement it. The hard parts: image rotation from EXIF, the cropping interaction, processing the cropped image at the right resolution, and the upload error handling. This guide covers what makes a good answer.

Clarify scope

  • Square crop only or any aspect ratio?
  • Single avatar or multiple sizes (thumbnail, banner, etc.)?
  • Drag-and-drop and click-to-select?
  • Mobile camera capture in scope?
  • Direct upload to S3 or via your server?

The picker

  • Hidden <input type="file" accept="image/*">
  • Click trigger; or drag-drop zone with onDragOver / onDrop
  • Multiple file types: PNG, JPG, WebP, HEIC (iOS), GIF (animated)
  • Size limit and validation (e.g., 10 MB max)
  • “Take a photo” on mobile via capture="user" attribute

The EXIF rotation trap

iOS photos often have an EXIF orientation flag rather than rotated pixels. Naive rendering shows them sideways:

  • Read EXIF data with a library (exifr, exif-parser)
  • Apply rotation in canvas before display
  • Modern browsers respect EXIF for <img> elements but not always for canvas; do not rely on it

The HEIC challenge

  • iOS photos are often HEIC, which Safari renders but Chrome / Firefox do not
  • Convert to JPEG / WebP server-side or with heic2any in the browser
  • Handle gracefully; do not show “broken image” if user uploads HEIC

The crop interaction

The standard pattern:

  • Image visible with a crop overlay (square or aspect-ratio constrained)
  • Drag the crop box to reposition
  • Pinch / wheel to zoom the underlying image
  • Apply / Cancel buttons

Use a library (react-easy-crop, react-image-crop) or implement with canvas + pointer events. For interview, a small custom one demonstrates skill; for production, the libraries are battle-tested.

Processing the crop

  1. Draw the original image to a hidden canvas
  2. Translate / scale based on crop selection
  3. Read back as a Blob using canvas.toBlob
  4. Specify output format (image/webp for size, image/jpeg for compatibility)
  5. Specify quality (0.8 is a reasonable default)

Output sizing

  • Most products use 256×256 or 512×512 for avatars
  • Generate multiple sizes server-side for responsive display
  • If client-side, use OffscreenCanvas for non-blocking processing

Upload

Direct-to-S3 (signed URL)

  • Server generates a signed PUT URL
  • Client uploads directly to S3 using the URL
  • Server records the resulting object key
  • No server bandwidth cost; faster for users

Upload through server

  • Client POSTs Blob to your API
  • Server validates, processes, stores
  • Higher control; higher bandwidth cost
  • Required if you do server-side image transformation

Progress and feedback

  • Show upload progress percentage (XMLHttpRequest or fetch with upload progress)
  • Optimistic UI: show preview immediately, replace with server URL on success
  • Error handling: surface clear errors; allow retry
  • Cancellation: AbortController if the user navigates away

Accessibility

  • File input is keyboard-accessible by default; do not break it
  • Crop interaction needs keyboard alternatives — arrow keys to move, +/- to zoom
  • Live region announces status: “Image selected, ready to crop”, “Uploading 50%”

Security and validation

  • Validate MIME type client-side and server-side
  • Reject obvious junk (text files renamed to .jpg)
  • Server-side: re-encode the image to strip metadata and exploits
  • Limit dimensions (10000×10000 → reject, prevents DoS)

Privacy concerns

  • EXIF data may include GPS coordinates of where the photo was taken
  • Strip EXIF before storing user-uploaded images
  • Document this in your privacy policy

Mobile considerations

  • iOS: capture="user" launches front camera; capture="environment" for back
  • Pinch zoom in the cropper; touch-friendly drag
  • Resize image before upload on mobile (data costs)

Edge cases interviewers love

  • User uploads a 50 MB photo — show error or auto-downscale before crop
  • User uploads animated GIF — preserve animation in some products, freeze in others
  • EXIF rotation — actually applies the rotation
  • Network drops mid-upload — retry with backoff
  • User navigates away mid-upload — abort, do not leave a half-uploaded blob

What separates senior from staff

Senior implementations handle the upload + crop. Staff implementations address EXIF rotation, HEIC conversion, accessible crop interactions, and the multi-size server pipeline. Principal candidates discuss the security implications (re-encoding, EXIF stripping) and the on-demand image-resize pipeline (CDN with image transforms).

Frequently Asked Questions

Library options for production?

react-easy-crop, react-image-crop, Cropper.js (vanilla). For React Native, react-native-image-crop-picker. For full image-CDN handling, Cloudinary or imgix.

Should I generate avatar sizes client-side?

Generally no — server-side image transforms (sharp, ImageMagick) are more reliable. Client-side is appropriate when you want to upload only the cropped result and avoid bandwidth.

What about animated avatars?

GIFs and short videos are increasingly common. Decide product policy: allow or freeze to first frame. For mid-tier products, freezing is the norm.

Scroll to Top