Load images
Pass a browser-readable URL as src. A new source loads and resets the cropper; clear it with null or undefined. Read isLoading() and isLoaded() reactively to drive your own UI, and handle onError so a failed load has a visible outcome.
Local files and URL ownership
A file input can create an object URL for the selected file. The application owns that URL: revoke it after replacement, clearing, or destruction, once no consumer needs it. Store the owned URL separately from src. Revoke the previous upload in the replacement or clear handler, and revoke the last upload in onDestroy; an effect can miss URLs replaced within the same update. Keep file-input resets in the change handler. Do not revoke the new URL immediately after assigning src.
The demo also detects the image format from its bytes, so export can choose an appropriate MIME type. Core extension imports such as advanced-cropper/extensions/mimes need advanced-cropper as a direct application dependency with strict package managers.
The library releases object URLs it creates internally for orientation correction. It does not revoke your src or URLs in an image you pass to setImage. That method takes a complete CropperImage value and replaces the displayed image without resetting geometry; use src for ordinary uploads.
Loading failures and orientation
checkOrientation defaults to true and reads supported EXIF orientation before displaying the photo. A revoked or unreadable blob URL reports onError, including when orientation checking is enabled. Clearing the source during loading clears the loading status.
Loading…
A remote source may fail because of CORS, even if a plain image element can display it. Check the network response and source readability before changing cropper settings.
HEIC and delayed file access
The cropper uses the browser’s decoder; it does not convert HEIC itself. This helper inspects the file, tries native decoding, and dynamically imports a converter only when needed. Keep conversion and loading error messages in your UI.
// Major brands that mark a HEIF container holding a HEIC photo, as iPhones save them.
const HEIC_BRANDS = new Set([
'heic',
'heix',
'hevc',
'hevx',
'heim',
'heis',
'hevm',
'hevs',
'mif1'
]);
/**
* Whether a file is HEIC, judged by its first 12 bytes: an `ftyp` box with a HEIC brand.
* Picked files can arrive without a name or MIME type, so neither is trusted.
*/
async function isHeic(file: Blob): Promise<boolean> {
const header = new Uint8Array(await file.slice(0, 12).arrayBuffer());
const boxType = String.fromCharCode(...header.subarray(4, 8));
const brand = String.fromCharCode(...header.subarray(8, 12));
return boxType === 'ftyp' && HEIC_BRANDS.has(brand);
}
/** Whether this browser decodes the image itself. Safari decodes HEIC; Chrome and Firefox don't. */
async function canDecode(file: Blob): Promise<boolean> {
try {
const bitmap = await createImageBitmap(file);
bitmap.close();
return true;
} catch {
return false;
}
}
/**
* Returns an object URL the cropper can load for a picked photo. A HEIC photo the browser
* can't decode is converted to JPEG first, with heic-to, which is only downloaded then.
* Any other photo is copied, because a picked file can stop being readable later. Rejects
* when the file can't be read or converted. Revoke the URL once the photo is replaced.
*/
export async function croppablePhotoUrl(file: File): Promise<string> {
if ((await isHeic(file)) && !(await canDecode(file))) {
const { heicTo } = await import('heic-to');
// The JPEG is built from bytes this page holds, so it's already a copy.
const jpeg = await heicTo({ blob: file, type: 'image/jpeg', quality: 0.92 });
return URL.createObjectURL(jpeg);
}
const copy = new Blob([await file.arrayBuffer()], { type: file.type });
return URL.createObjectURL(copy);
}Loading…
A picked File can become unreadable if its underlying file changes or access expires. If cropping happens later, copy the bytes immediately while access is available. The helper above does that for unconverted files too. The copy uses memory until its object URL is released. Invalidate pending preparation when the demo is destroyed or a preset is selected. If an older preparation finishes later, revoke its URL immediately instead of displaying it.
Image replacement can race with asynchronous conversion. Keep ownership of each request and release results that arrive after a newer selection; the example includes that cleanup.