Vue Picture Cropper
Vue Picture Cropper is a lightweight and easy-to-use image cropping component specifically designed for Vue 3 applications. It leverages the popular Cropper.js library internally, providing a robust set of cropping functionalities. The current stable version is `v1.0.0`, which introduced significant breaking changes and new features, most notably the `useCropper` composable for enhanced programmatic control. The project maintains an active release cadence, with a focus on Vue 3 Composition API, TypeScript support, and features like multiple cropper instances on a single page, preset cropping modes (fixed size, circular), and direct output as Blob, File, or Base64. It differentiates itself through its tight integration with the Vue 3 ecosystem and a streamlined API for common cropping tasks.
Common errors
-
Cannot find module 'cropperjs' or its corresponding type declarations.
cause The `cropperjs` library, a peer dependency, has not been installed.fixInstall `cropperjs`: `npm install cropperjs@^1` or `yarn add cropperjs@^1`. -
TypeError: (0, vue_picture_cropper__WEBPACK_IMPORTED_MODULE_0__.useCropper) is not a function
cause Attempting to use the `useCropper` composable on a version of `vue-picture-cropper` prior to `v1.0.0`, or an incorrect named import.fixUpgrade `vue-picture-cropper` to `v1.0.0` or higher (`npm install vue-picture-cropper@^1`) and ensure it's imported correctly as a named export: `import { useCropper } from 'vue-picture-cropper'`. -
Property 'getBlob' does not exist on type '(...)' (TypeScript error)
cause This error often occurs when `getBlob()` is called synchronously or on an undefined cropper instance, or when the TypeScript context doesn't correctly infer the `CropperResult` type.fixEnsure you are awaiting `getBlob()` as it's an asynchronous operation. Also, make sure the `cropper` instance (e.g., obtained from `useCropper().getCropper()`) is valid before attempting to call methods on it.
Warnings
- breaking Version 1.0.0 introduced significant breaking changes, including enhanced type constraints, unified public exports, component-scoped cropper instances replacing module-level ones, and the introduction of the `useCropper` composable. Direct access to the Cropper.js instance and event handling might have changed.
- gotcha The `cropperjs` library is a peer dependency and must be installed separately alongside `vue-picture-cropper`. Forgetting to install it will lead to runtime errors.
- gotcha The `getBlob`, `getFile`, and `getBase64` methods are asynchronous operations since v0.3.0 (for `getBlob`) due to their reliance on canvas processing to generate higher quality output and correctly handle large images, which was a fix for previous white-border issues.
- gotcha Prior to v0.1.11, if cropping failed and `base64` was empty, converting it to a `blob` could lead to application crashes. This was fixed to return `null` instead.
Install
-
npm install vue-picture-cropper -
yarn add vue-picture-cropper -
pnpm add vue-picture-cropper
Imports
- PictureCropper
import { PictureCropper } from 'vue-picture-cropper'import PictureCropper from 'vue-picture-cropper'
- useCropper
import useCropper from 'vue-picture-cropper'
import { useCropper } from 'vue-picture-cropper' - CropperResult
import { CropperResult } from 'vue-picture-cropper'import type { CropperResult } from 'vue-picture-cropper'
Quickstart
<!-- App.vue -->
<template>
<div class="container">
<h1>Image Cropper</h1>
<input type="file" accept="image/*" @change="selectFile" />
<div v-if="imageSrc">
<PictureCropper
:boxStyle="{ width: '100%', height: '300px', backgroundColor: '#f8f8f8', margin: 'auto' }"
:img="imageSrc"
:options="{
viewMode: 1,
dragMode: 'move',
aspectRatio: 1 / 1,
cropBoxResizable: true
}"
@ready="cropperReady"
/>
<button @click="cropImage">Crop Image</button>
<div v-if="croppedImageBlob">
<h2>Cropped Image Preview</h2>
<img :src="croppedImageBlob" alt="Cropped" style="max-width: 100%;" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import PictureCropper, { useCropper } from 'vue-picture-cropper';
const imageSrc = ref<string | null>(null);
const croppedImageBlob = ref<string | null>(null);
const { getCropperRef, getCropper, setCropper } = useCropper();
const selectFile = (e: Event) => {
const target = e.target as HTMLInputElement;
if (target.files && target.files.length) {
const file = target.files[0];
imageSrc.value = URL.createObjectURL(file);
}
};
const cropperReady = () => {
console.log('Cropper is ready!');
// You can set options or perform actions when cropper is ready
// For example, setCropper(getCropperRef().value.cropperInstance); // If you need direct Cropper.js instance
};
const cropImage = async () => {
const cropper = getCropper(); // Get the current cropper instance via composable
if (cropper) {
const blob = await cropper.getBlob();
if (blob) {
croppedImageBlob.value = URL.createObjectURL(blob);
console.log('Cropped image blob:', blob);
} else {
console.error('Failed to get cropped image blob.');
}
}
};
</script>
<style>
.container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
font-family: sans-serif;
}
button {
margin-top: 20px;
padding: 10px 15px;
background-color: #3fb984;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>