Vue Image Cropper Component
Vue-croppa is a straightforward, highly customizable, and mobile-friendly image cropping component specifically designed for Vue 2.0 applications. It enables users to select an image, move, zoom, and crop it directly within the browser, supporting touch gestures for mobile devices and handling EXIF orientation. The current stable version is 1.3.8. While the package has seen consistent bug fixes and minor feature additions (like `auto-sizing` and `passive` mode) in its 1.x series, its focus on Vue 2.0 suggests a maintenance cadence rather than active feature development for newer Vue versions. Its key differentiators include simplicity of use, extensive customization options via props, and built-in mobile support for common cropping interactions.
Common errors
-
ReferenceError: window is not defined
cause The package attempts to access the `window` object during server-side rendering (SSR), which is only available in a browser environment.fixImplement SSR-specific checks or dynamically import the component only on the client-side. For Nuxt.js, use `<client-only><croppa ...></client-only>` or register the component as a client-side plugin. -
TypeError: Cannot read properties of undefined (reading 'hasImage')
cause This usually occurs when the `v-model` bound `croppa` object has not been properly initialized or the component hasn't rendered yet, meaning the API methods are not available.fixEnsure `myCroppa: {}` is defined in your component's `data` property, and any calls to `myCroppa` methods are made after the component is mounted or after an image has been loaded (e.g., in a method triggered by user interaction). -
Error: Component name "croppa" has already been registered.
cause Attempting to call `Vue.use(Croppa)` or `Vue.component('croppa', Croppa.component)` multiple times or in different modules within the same Vue instance.fixEnsure `Vue.use(Croppa)` is called only once, typically in your main `main.js` or `app.js` file. If using multiple Vue apps, register it separately for each, or customize the component name to avoid clashes.
Warnings
- breaking The `v-model` behavior for the cropper component has undergone changes. Prior to v1.0.0, the `v-model` prop might have behaved differently or had different expectations regarding the component's internal state binding.
- gotcha The `replace-drop` prop, which controls whether dropping a new image replaces the current one without explicit removal, defaults to `false` for backward compatibility. This might lead to unexpected behavior for users expecting the new image to automatically replace the old one.
- gotcha When using `Vue.use(Croppa)`, the component is globally registered under the name `<croppa>`. If this name conflicts with existing components or you prefer a custom name, you need to specify it during plugin installation.
- gotcha The `width` and `height` props are ignored if `auto-sizing` is set to `true`. In this mode, the cropper will adjust to its container's size, which can be useful for responsive layouts but might override explicit dimensions.
- gotcha The component is primarily designed for Vue 2.0. While it might work in a compatibility layer for Vue 3, official support for Vue 3 or Composition API is not explicitly stated, potentially leading to integration issues.
Install
-
npm install vue-croppa -
yarn add vue-croppa -
pnpm add vue-croppa
Imports
- Croppa
import { Croppa } from 'vue-croppa';import Croppa from 'vue-croppa';
- Croppa styles
import 'vue-croppa/dist/vue-croppa.css';
- Vue.use(Croppa)
Vue.use(Croppa);
Quickstart
import Vue from 'vue';
import Croppa from 'vue-croppa';
import 'vue-croppa/dist/vue-croppa.css';
Vue.use(Croppa);
new Vue({
el: '#app',
template: `
<div>
<croppa v-model="myCroppa" :width="400" :height="400" :placeholder="'Drag & Drop or Click to Choose'" :quality="2"></croppa>
<button @click="uploadCroppedImage">Upload Cropped Image</button>
</div>
`,
data: {
myCroppa: {}
},
methods: {
uploadCroppedImage() {
if (!this.myCroppa.hasImage()) {
alert('Please choose an image first!');
return;
}
this.myCroppa.generateBlob(
blob => {
// In a real application, you'd upload this blob to a server.
// For demonstration, we'll just log its size and type.
console.log('Generated Blob:', blob);
console.log(`Type: ${blob.type}, Size: ${blob.size} bytes`);
// Example: const formData = new FormData();
// formData.append('image', blob, 'cropped-image.jpeg');
// fetch('/upload', { method: 'POST', body: formData });
alert('Image blob generated and logged to console!');
},
'image/jpeg',
0.8
); // 80% compressed jpeg file
}
}
});