The problem
A grid of avatars looks wrong the moment one is a tall portrait and another is a wide landscape. Forcing the shape in CSS with object-fit helps, but you still download the full upload, and you cannot control where the crop lands.
Doing it on the server side of the image is better: the browser receives an image that is already the right shape and size, and a phone does not download a 6 MB photo to show a 48 pixel circle.
Option 1: resize with cover
The simplest approach is a fixed box with fit:cover. The image is scaled to fill the box and the overflow is cropped. Use position to choose which edge to favour.
?transform=resize:200x200~fit:cover
?transform=resize:200x200~fit:cover~position:topposition:top is useful for portraits, where the face is usually in the upper part of the frame.
Option 2: show the whole image on a plain background
When nothing may be cut off, such as logos or product shots, use fit:contain. The whole image is scaled to fit and the leftover space is filled with a colour. Colours use hex(...) with 3, 6 or 8 digits.
?transform=resize:200x200~fit:contain~background:hex(fff)Which crop to use
- Avatars and cards:
fit:cover, withposition:topfor portraits. - Logos and product images:
fit:containwith a background colour. - Exact pixel size, any shape:
fit:fillstretches the image, so use it only when distortion does not matter. - Logos and product shots with white space:
trimfirst to cut the plain border away, thenfit:contain. - A chosen crop region: save the point that matters (from a crop tool in your uploader, say) and pass it as
~focus:X,Yon the resize, or crop exactly withcrop:WxH@X,Y. - Face detection: the image service does not detect faces.
smartCroppicks the most detailed part of the image, which often but not always includes the face.
In React
import { Image } from "@cd2/fileboy-react"
export function Avatar({ fileId, size = 48 }: { fileId: string; size?: number }) {
// Ask for twice the displayed size so it stays sharp on high-density screens.
const pixels = size * 2
return (
<Image
id={fileId}
transforms={[
{ type: "resize", width: pixels, height: pixels, fit: "cover", position: "top" },
{ type: "format", format: "webp" },
{ type: "quality", value: 80 },
]}
imgProps={{ width: size, height: size, alt: "", style: { borderRadius: "50%" } }}
/>
)
}The same pattern builds thumbnails of any shape: change the transform and the size, and keep the stored file ID the same. Because the original is untouched, you can change the avatar size across your whole app in one edit.
From your server
Emails, APIs and server-rendered pages need the URL without React. Build it with the client for your language, using the same resize and fit options.
const avatar = fileboy.buildImageUrl(photoId, {
transforms: [{ type: "resize", width: 200, height: 200, fit: "cover" }],
})avatar = fileboy.build_image_url(photo_id, transforms: [
{ type: :resize, width: 200, height: 200, fit: :cover }
])avatar = client.build_image_url(photo_id, transforms=[
{"type": "resize", "width": 200, "height": 200, "fit": "cover"},
])$avatar = $fileboy->buildImageUrl($photoId, transforms: [
['type' => 'resize', 'width' => 200, 'height' => 200, 'fit' => 'cover'],
]);Checked against the current libraries in November 2026. Found something that no longer works? Tell us.