TrueCropper.js
Lightweight, real-size image, vanilla JS image cropping tools
Features:
- Made only with native, delicious vanilla JS
- Zero dependencies
- No z-index, make modals easy
- Work with real image pixel size
- Supports touch devices
- Set results in dataset of image
Installation
npm install truecropper
Basic usage
<img src="image.jpg" id="myImageCropper" />
<script>
// import the library
import TrueCropper from "truecropper";
import "truecropper/dist/truecropper.css"; // or import "truecropper.scss"
//.. other code ..//
const cropper = new TrueCropper("#myImageCropper");
const result = cropper.getValue();
</script>
Working with Image Sizes in the Library
In a browser, each image has two types of sizes:
- Original size — the actual resolution of the image (e.g., 2048×2048).
- Displayed size — the size at which the image is rendered on the page (e.g., 512×512).
Getting Image Sizes in JavaScript
- Displayed size can be obtained using
Image.width
. - Original size (
Image.naturalWidth
andImage.naturalHeight
) becomes available only after the image has loaded. Attempting to access it earlier will return0
, which may cause a race condition. This is particularly important when initializing tools such as cropping.
Unlike other cropping tools, the library provides three modes for working with image sizes:
- Real (real) — operates with pixels of the original image
(
naturalWidth
,naturalHeight
). - Percentage-based (percent) — works with percentages of the original size.
- Relative (relative) — uses pixels based on the displayed size of the image on the page.
Cropping Initialization
When initialized, the cropper modifies the DOM by replacing the <img>
element with a <div class="truecropper">
. It then adds the original <img>
inside this <div>
.
To prevent this behavior, add the truecropper
class to the parent
element.
<div class="truecropper"><img src="image.jpg" id="myImageCropper"/></div>
Component Settings
Settings can be specified in two ways:
Method 1
Create an instance of the component by passing an object with the settings, for example:
const cropper = new TrueCropper("#imageId", { aspectRatio: 500/400, ... });
Method 2
Specify the parameters directly in the dataset attribute of the element with the id imageId
,
for example:
<img src="..." data-truecropper-aspect-ratio="0.4" />
In this case, the settings specified via the dataset have higher priority.
Retrieve Crop Region
The library supports two methods for obtaining the crop result. This flexibility allows you to choose the method that best fits your application's needs.
Method 1: Use the getValue()
method.
When you call getValue()
on your cropper instance, it returns an object containing the current
crop region’s coordinates and dimensions. This method is ideal if you want a simple, programmatic way to get
the crop data.
const cropper = new TrueCropper("#myImage", {});
const result = cropper.getValue();
// result might be: { x: 10, y: 20, width: 200, height: 150 }
Method 2: Retrieve the values directly from the image's dataset.
The crop values are stored as data attributes on the image element. You can access these values by querying
the element’s dataset
. This method is useful if you need to work directly with the DOM or
integrate with other scripts that expect data attributes.
const dataset = document.querySelector("#myImage").dataset;
const cropData = {
x: dataset.truecropperX,
y: dataset.truecropperY,
width: dataset.truecropperWidth,
height: dataset.truecropperHeight
};
Notes on Percentage Values
If you use percentage values for the crop region, note that the cropper does not return a decimal (e.g.,
0.8
) but rather the whole number percentage (e.g., 80
), representing 80%.
Similarly, when using the setValue
method to define the crop region, you should provide
percentage values as integers (for example, 80
) rather than as decimals (for example,
0.8
).
Crop Component Parameters: startSize
vs. defaultSize
The startSize
parameter is used during the initial initialization of the crop component to set
the starting dimensions and position of the cropping area. The defaultSize
parameter, on the
other hand, is used to initialize the component when a new image is loaded.
If defaultSize
is provided and startSize
is not, then defaultSize
will be used as the initial settings.
So why is the startSize
parameter needed? Consider the following example. Suppose you have an
avatar upload form where the default crop settings are defined as:
{ x: 0, y: 0, width: 50%, height: 50% }
.
A user uploads an image with crop settings of:
{ x: 10%, y: 10%, width: 50%, height: 50% }
.
Later, when the edit form is opened, you need to display the crop settings
{ x: 10%, y: 10%, width: 50%, height: 50% }
that the user previously set. However, if the image
is changed, you want to revert to the default crop settings, for example with centered coordinates and
dimensions { width: 50%, height: 50% }
.
To address this scenario, you should set:
-
startSize
:{ x: 10%, y: 10%, width: 50%, height: 50% }
– to display the crop settings saved by the user during editing. -
defaultSize
:{ x: 0, y: 0, width: 50%, height: 50% }
– to be used when a new image is loaded.
How to center crop area
If the x
or y
coordinates are not specified in the startSize
or
defaultSize
settings, then after the component is initialized, the crop area is automatically
centered.
Touch Devices CSS Behavior
On touch devices, the control elements (handles
) used to move the crop area are enlarged
threefold to provide easier interaction with less precise input methods. If you want to change this
behavior, modify the following CSS code:
@media (hover: none) and (pointer: coarse) {
.truecropper__handle {
width: 30px;
height: 30px;
}
}
This media query applies only to devices that do not support hover and use coarse pointers—a typical characteristic of touch screens. Here, the control points are set to 30px in both width and height, but you can adjust these values to better suit your requirements.
What is epsilon
When running the program, values containing subpixels are generated. The returned value is rounded to whole pixels, which may lead to the image’s aspect ratio (width/height) on the server not matching the expected aspectRatio.
To correct these distortions and other rounding errors when working with floating-point numbers, the following check is used:
if (Math.abs(width / height - aspectRatio) < epsilon) {
// The value is considered valid
}
Here, epsilon is a small positive value that accounts for calculation inaccuracies. The
default value for epsilon is 0.05
, but you can adjust it in the options to
suit your
requirements. This approach ensures that even minor rounding errors in the computed aspect ratio are
considered acceptable.
Using the onError
function
You can set an onError
function for the cropper to handle errors that occur during the
initialization or when the image's src
is updated. For example:
new TrueCropper("#imageId", {
onError: (instance, error) => {
// some code
}
});
This function is triggered primarily when there are errors in the cropper's initialization or when the image's
src
is updated. Refer to the documentation for a list of possible errors. For instance, this
function can be useful for handling a minSize
error if the image is smaller than the specified
minSize
.
Maximum image height recommendation
By default, the cropper does not have a maximum image height set in the DOM. It is recommended that you set it manually. For example:
.my-image {
max-height: 300px;
}
Demo

Examples
moveTo, setValue, resizeTo, scaleBy functions
This example illustrates various methods for managing the cropping area.

code
const imageElement = document.querySelector("#example4") as HTMLImageElement;
const cropper = new TrueCropper(imageElement);
document.querySelector("#example4MoveLeft")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
cropper.moveTo({ x: currentValue.x - 10, y: currentValue.y });
})
document.querySelector("#example4MoveRight")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
cropper.moveTo({ x: currentValue.x + 10, y: currentValue.y });
})
document.querySelector("#example4MoveTop")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
cropper.moveTo({ x: currentValue.x, y: currentValue.y - 10 });
})
document.querySelector("#example4MoveBottom")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
cropper.moveTo({ x: currentValue.x, y: currentValue.y + 10 });
})
document.querySelector("#example4ResizeLeftTop")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
// for example use setValue
const status = cropper.setValue({ x: currentValue.x - 10, y: currentValue.y - 10, width: currentValue.width + 10, height: currentValue.height + 10 });
console.log(status);
})
document.querySelector("#example4ResizeLeftBottom")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
// for example use setValue
const status = cropper.setValue({ x: currentValue.x - 10, y: currentValue.y, width: currentValue.width + 10, height: currentValue.height + 10 });
console.log(status);
})
document.querySelector("#example4ResizeRightTop")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
// for example use resizeTo
const status = cropper.resizeTo({ width: currentValue.width + 10, height: currentValue.height + 10 }, { x: 0, y: 1 });
console.log(status);
})
document.querySelector("#example4ResizeRightBottom")?.addEventListener("click", () => {
const currentValue = cropper.getValue();
// for example use resizeTo
const status = cropper.resizeTo({ width: currentValue.width + 10, height: currentValue.height + 10 }, { x: 0, y: 0 });
console.log(status);
})
document.querySelector("#example4DownScale")?.addEventListener("click", () => {
// { x: 0.5, y: 0.5 } is center
// { x: 0, y: 1 } is left bottom
// { x: 1, y: 0 } is right top
// { x: 0.4, y: 0.3 } is 40% from left, is 30% from top
const status = cropper.scaleBy(0.5, { x: 0.5, y: 0.5 });
console.log(status);
})
document.querySelector("#example4UpScale")?.addEventListener("click", () => {
const status = cropper.scaleBy(2);
console.log(status);
})
Image preview
This example demonstrates how to implement an image preview feature.

code
const imageElement = document.querySelector("#example5") as HTMLImageElement;
const imageElementPreview = document.querySelector("#examplePreview5") as HTMLImageElement;
const cropper = new TrueCropper(imageElement, {
onCropEnd: (instance, data) => {
const canvas = instance.getImagePreview();
if (canvas) {
imageElementPreview.src = canvas.toDataURL();
}
}
});
Image replace via file input
The cropper supports replacing the image through a file input element. You can attach an event handler to the file input so that when a user selects a new image, it automatically loads into the cropper, replacing the current image.

code
// Select the image element for cropping
const imageElement = document.querySelector("#myImage") as HTMLImageElement;
const cropper = new TrueCropper(imageElement);
// Select the file input element
const fileInput = document.querySelector("#myFileInput");
// Add an event listener for file input change
fileInput?.addEventListener("change", (event) => {
const target = event.target as HTMLInputElement;
const files = target.files;
// Exit if no files are selected
if (!files || files.length === 0) return;
const reader = new FileReader();
reader.onload = (event) => {
const result = event.target?.result;
if (!result) return;
// Set the cropped image source
cropper.setImage(result.toString());
// Alternatively, update the image element's source directly
// imageElement.src = result.toString();
};
// Read the selected file as a data URL
reader.readAsDataURL(files[0]);
});
Modal
This example demonstrates how to change the image using a button, which then opens a modal window where the cropping interface is presented. This approach allows the user to work with the image in a dedicated, isolated interface, improving the editing experience and ensuring that the cropping functionality is easily accessible.
code
// Select the image element for cropping
const imageElement = document.querySelector("#example2") as HTMLImageElement;
const imageElementButton = document.querySelector("#exampleButtonImage2") as HTMLImageElement;
const cropper = new TrueCropper(imageElement, {
aspectRatio: 1,
onCropEnd: (instance, data) => {
const canvas = instance.getImagePreview();
if(canvas) {
imageElementButton.src = canvas.toDataURL();
}
},
});
imageElementButton.src = imageElement.src;
// Select the file input element
const fileInput = document.querySelector("#exampleInput2");
// Add an event listener for file input change
fileInput?.addEventListener("change", (event) => {
const target = event.target as HTMLInputElement;
const files = target.files;
// Exit if no files are selected
if (!files || files.length === 0) return;
const reader = new FileReader();
reader.onload = (event) => {
const result = event.target?.result;
if (!result) return;
// Set the cropped image source
cropper.setImage(result.toString());
};
// Read the selected file as a data URL
reader.readAsDataURL(files[0]);
});
onError
This example shows how to use the onError handler to catch errors that occur during the cropper's operation. For instance, if the loaded image is smaller than the specified minSize, the onError function will be triggered. This allows you to implement additional logic, such as displaying an error message or reverting to alternative settings, to handle such cases gracefully.
The minimum size of image is 500x500
code
const errorDiv = document.querySelector("#exampleError3") as HTMLImageElement;
// Select the image element for cropping
const imageElement = document.querySelector("#example3") as HTMLImageElement;
const cropper = new TrueCropper(imageElement, {
minSize: {
width: 500,
height: 500
},
onError: (instance, error) => {
errorDiv.innerText = error.message;
errorDiv.classList.remove('d-none');
imageElement.classList.add('d-none');
}
});
// Select the file input element
const fileInput = document.querySelector("#exampleInput3");
// Add an event listener for file input change
fileInput?.addEventListener("change", (event) => {
const target = event.target as HTMLInputElement;
const files = target.files;
// Exit if no files are selected
if (!files || files.length === 0) return;
const reader = new FileReader();
reader.onload = (event) => {
const result = event.target?.result;
if (!result) return;
errorDiv.innerText = "";
errorDiv.classList.add('d-none');
// Set the cropped image source
// cropper.setImage(result.toString());
imageElement.src = result.toString();
imageElement.classList.remove('d-none');
};
// Read the selected file as a data URL
reader.readAsDataURL(files[0]);
});
Options
Configuration options
aspectRatio
Sets a fixed aspect ratio for the crop region.
- Type: Number
- Default: null
- Example:
aspectRatio: 1
(for a square crop) - Dataset attribute name:
data-truecropper-aspect-ratio
epsilon
Epsilon is a small positive value that takes into account the inaccuracies in the calculations of the aspect.
- Type: Number
- Default: 0.05
- Example:
epsilon: 0.005
- Dataset attribute name:
data-truecropper-epsilon
maxSize
Defines the maximum allowable dimensions for the crop region.
- Type: { width?, height?, unit? }
- Default: null
- Example:
maxSize: [50, 50, 'percent']
(limits to 50% of the image size) -
Dataset attribute names:
data-truecropper-max-size-width
data-truecropper-max-size-height
data-truecropper-max-size-unit
Note: unit
accepts a value of 'real', 'percent', or
'relative'. Defaults to 'real'.
minSize
Specifies the minimum dimensions for the crop region.
- Type: { width?, height?, unit? }
- Default: null
- Example:
minSize: [20, 20, 'real']
(minimum dimensions of 20px by 20px) -
Dataset attribute names:
data-truecropper-min-size-width
data-truecropper-min-size-height
data-truecropper-min-size-unit
Note: unit
accepts a value of 'real', 'percent', or
'relative'. Defaults to 'real'.
startSize
Determines the initial crop region when the cropper is first initialized.
- Type: { x?, y?, width?, height?, unit? }
- Default: [0, 0, 100, 100, 'percent'] (maximizes the crop region)
- Example:
startSize: [0, 0, 50, 50, "real"]
-
Dataset attribute names:
data-truecropper-start-size-x
data-truecropper-start-size-y
data-truecropper-start-size-width
data-truecropper-max-start-height
data-truecropper-start-size-unit
Note: unit
accepts a value of 'real', 'percent', or
'relative'. Defaults to 'real'.
Note2: If x
is not defined, the crop region is centered horizontally. If y
is
undefined, it is centered vertically.
Note3: If startSize
is undefined, for the first initialized used defaultSize
.
defaultSize
Specifies the crop region size for subsequent initializations (e.g., after calling
instance.setImage()
).
- Type: { x?, y?, width?, height?, unit? }
- Default: [0, 0, 100, 100, 'percent'] (A starting crop region as large as possible)
- Example:
defaultSize: [50, 50]
(positioned at 50,50 with maximum crop area) -
Dataset attribute names:
data-truecropper-default-size-x
data-truecropper-default-size-y
data-truecropper-default-size-width
data-truecropper-max-default-height
data-truecropper-default-size-unit
Note: unit
accepts a value of 'real', 'percent', or
'relative'. Defaults to 'real'.
Note2: If x
is undefined, the crop region is centered horizontally. If y
is
undefined, it is centered vertically.
returnMode
Determines how the crop region values are returned.
- Type: String
- Default: "real"
-
Possible values:
real
: Returns values based on the image's original dimensions.percent
: Returns values as a percentage.relative
: Returns values based on the image's displayed size in the DOM.
- Dataset attribute name:
data-truecropper-return-mode
allowFlip
Determines whether the crop selection can be flipped horizontally and vertically.
- Type: Boolean
- Default: true
- Example:
allowFlip: false
- Dataset attribute name:
data-truecropper-allow-flip
allowNewSelection
Controls whether the user can create a new selection on the image.
- Type: Boolean
- Default: true
- Example:
allowNewSelection: false
- Dataset attribute name:
data-truecropper-allow-new-selection
allowMove
Enables or disables the ability to move the crop selection around the image.
- Type: Boolean
- Default: true
- Example:
allowMove: true
- Dataset attribute name:
data-truecropper-allow-move
allowResize
Controls whether the crop selection can be resized.
- Type: Boolean
- Default: true
- Example:
allowResize: true
- Dataset attribute name:
data-truecropper-allow-resize
Callback Functions
onInitialize
Invoked before the cropping interface is rendered.
- Type: Function
- Arguments:
instance
(the current cropper instance),data = { x, y, width, height }
- Example:
onInitialize: function(instance, data) {
// do things here
}
onCropStart
Called when the user begins modifying the crop region.
- Type: Function
- Arguments:
instance
,data = { x, y, width, height }
- Example:
onCropStart: function(instance, data) {
console.log(data.x, data.y, data.width, data.height);
}
onCropChange
Fires continuously as the crop region is being adjusted.
- Type: Function
- Arguments:
instance
,data = { x, y, width, height }
- Example:
onCropChange: function(instance, data) {
console.log(data.x, data.y, data.width, data.height);
}
onCropEnd
Triggered when the user finishes adjusting the crop region.
- Type: Function
- Arguments:
instance
,data = { x, y, width, height }
- Example:
onCropEnd: function(instance, data) {
console.log(data.x, data.y, data.width, data.height);
}
onError
Invoked when an error occurs during initialization or when the image source is updated.
- Type: Function
- Arguments:
instance
,data = CallbackError
(see CallbackError below) - Example:
onError: function(instance, data) {
console.error(data.message);
}
Methods
The following methods enable interaction with the TrueCropper instance:
getValue(returnMode?: string): { x, y, width, height }
Returns the crop region's values, rounded to whole numbers. If no returnMode
is specified, it
defaults to the option provided during initialization.
const value = cropInstance.getValue();
// value = { x: 21, y: 63, width: 120, height: 120 }
const percentValue = cropInstance.getValue("percent");
// percentValue = { x: 10, y: 30, width: 57, height: 57 }
getImagePreview(): HTMLCanvasElement
Returns a preview of the cropped image.
const canvas = cropper.getImagePreview();
imagePreview.src = canvas.toDataURL();
getImageProps(): { real: { width, height }, relative: { width, height } }
Returns an object with the image's dimensions in both its real and relative sizes:
{ real: { width, height }, relative: { width, height } }
.
If the status is not ready
, it returns zero or the previous image values for width and height.
getStatus(): TrueCropperStatus
Retrieves the current status of the TrueCropper instance.
moveTo({ x, y }, mode?): void
Moves the crop region to the specified coordinates.
setValue({ x, y, width, height }, mode?): { ok, message }
Sets the crop region with the specified properties.
resizeTo({ width, height }, { x, y }?, mode?): { ok, message }
Resizes the crop region to the specified dimensions.
The optional { x, y }
parameter specifies the origin
point for resizing. Defaults to { x: 0.5, y: 0.5 }
(center).
scaleBy(factor, { x, y }?, mode?): { ok, message }
Scales the crop region by the given factor.
The optional { x, y }
parameter specifies the origin
point for resizing. Defaults to { x: 0.5, y: 0.5 }
(center).
setImage(src: string)
Changes the image source to the specified URL.
reset()
Resets the crop region to its default position and size.
destroy()
Destroys the TrueCropper instance and restores the original <img>
element.
Statuses
TrueCropper.js maintains its current status in the image's dataset, and you can also query it using the
getStatus()
method. The possible statuses are:
- waiting: Waiting to initialize.
- ready: Ready to work.
- reloading: Image uploading in progress.
- error: Error during initialization.
Errors
When an image is initialized or reloaded, TrueCropper.js validates the initialization parameters.
If an error occurs, the onError
callback is fired (if set); otherwise, the error propagates.
List of possible errors:
-
DOM element specified in selector not found
- Name:
TrueCropperHtmlError
- Message: "Unable to find element"
- Message ID: 0
- Data: {}
- Name:
-
The DOM element is not an image element
- Name:
TrueCropperHtmlError
- Message: "Image src not provided"
- Message ID: 1
- Data: {}
- Name:
-
A DOM element has no parent DOM element (usually, this error is rare as the element is at
least in the body tag)
- Name:
TrueCropperHtmlError
- Message: "Parent element can be exists"
- Message ID: 2
- Data: {}
- Name:
-
The type of configuration parameters or dataset does not match the required data type
- Name:
TrueCropperOptionsError
- Message: "{name} must be {object}"
- Message ID: 3
- Data: { name, object }
- Name:
-
Configuration parameters or dataset contain an invalid value (e.g., NaN)
- Name:
TrueCropperOptionsError
- Message: "{name} must not be {object}"
- Message ID: 4
- Data: { name, object }
- Name:
-
Floating point rounding errors in aspect ratio
- Name:
TrueCropperOptionsError
- Message: "The specified aspect ratio {aspectRatio} and calculated {name} dimensions width/height = {calculatedAspectRatio} are greater than {epsilon}. This might be due to a rounding error on the server side or incorrect sizes"
- Message ID: 5
- Data: { name }
- Name:
-
Start or default crop settings are not suitable for the current image
- Name:
TrueCropperImageError
- Message: "The startSize {x}x{y}:{width}x{height} exceeds the imageSize {imageSize.width}x{imageSize.height}"
- Message ID: 6
- Data: { target: "startSize", coordinates: { x, y }, targetSize: { width, height }, source: "imageSize", sourceSize: { width, height } }
- Name:
-
Image size, maxSize, or startSize is smaller than minSize
- Name:
TrueCropperImageError
- Message: "The minSize {width}x{height} exceeds the {source} {source.width}x{source.height}"
- Message ID: 7
- Data: { target: "minSize", targetSize: { width, height }, source: "imageSize|maxSize|startSize", sourceSize: { width, height } }
- Name:
-
Height is provided but width is null
- Name:
TrueCropperOptionsError
- Message: "The width of {name} is null"
- Message ID: 8
- Data: { name }
- Name:
-
Width is provided but height is null
- Name:
TrueCropperOptionsError
- Message: "The height of {name} is null"
- Message ID: 9
- Data: { name }
- Name:
-
Percent values exceed 100
- Name:
TrueCropperOptionsError
- Message: "The percent values of {name} > 100"
- Message ID: 10
- Data: { name }
- Name: