mainul12501 / drag-drop-crop-image-upload
Laravel drag, drop, and crop image upload widget.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Language:JavaScript
pkg:composer/mainul12501/drag-drop-crop-image-upload
Requires
- php: ^8.1
- illuminate/support: ^11.0|^12.0
README
A Laravel package that provides an elegant drag-and-drop image upload widget with built-in cropping functionality. Perfect for profile pictures, avatars, and any image upload that requires user-controlled cropping.
Features
- Drag & drop image upload
- Interactive image cropping with Cropper.js
- Image rotation (90° left/right)
- Configurable aspect ratio, dimensions, and quality
- Base64 to file conversion with automatic format detection
- Bootstrap modal integration support
- Customizable labels and UI text
- Auto-cleanup of old images on re-upload
Requirements
| Requirement | Version |
|---|---|
| PHP | 8.1 - 8.5 |
| Laravel | 11.x, 12.x |
Installation
Via Composer (Packagist)
composer require mainul12501/drag-drop-crop-image-upload
Publish the assets and configuration:
php artisan vendor:publish --tag=drag-drop-crop-assets php artisan vendor:publish --tag=drag-drop-crop-config
Local Development
Add to your main app's composer.json:
{
"repositories": [
{
"type": "path",
"url": "packages/drag-drop-crop-image-upload"
}
],
"require": {
"mainul/drag-drop-crop-image-upload": "*"
}
}
Then run:
composer update php artisan vendor:publish --tag=drag-drop-crop-assets php artisan vendor:publish --tag=drag-drop-crop-config
Quick Start
1. Render the Widget in Your Blade View
{!! DragDropCrop::generateUpload('profile_image') !!}
2. Handle the Upload in Your Controller
use DragDropCropImageUpload\Facades\DragDropCrop; public function store(Request $request) { $imagePath = DragDropCrop::uploadFromBase64( $request->input('profile_image'), 'uploads/avatars' ); // Save $imagePath to your database User::create([ 'avatar' => $imagePath, // ... other fields ]); }
Usage
Basic Usage
{!! DragDropCrop::generateUpload('field_name') !!}
With Custom Options
{!! DragDropCrop::generateUpload('profile_image', [
'aspect_ratio' => 1, // 1:1 square (use 16/9 for widescreen)
'crop_width' => 300, // Output width in pixels
'crop_height' => 300, // Output height in pixels
'max_size_mb' => 5, // Maximum file size in MB
'quality' => 0.8, // JPEG quality (0.1 to 1.0)
]) !!}
Inside Bootstrap Modal
{!! DragDropCrop::generateUpload('avatar', [
'modal_id' => 'profileModal', // Resets widget when modal closes
]) !!}
Custom Labels
{!! DragDropCrop::generateUpload('photo', [
'labels' => [
'title' => 'Upload Your Photo',
'browse' => 'Click to select',
'supports' => 'JPG, PNG (Max 10MB)',
'cropped_title' => 'Preview:',
'change' => 'Choose Different',
'reset' => 'Start Over',
'rotate_left' => 'Rotate Left',
'rotate_right' => 'Rotate Right',
'crop' => 'Crop & Save',
],
]) !!}
API Reference
DragDropCrop::generateUpload(string $inputName, array $options = [])
Renders the drag-drop-crop widget.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$inputName |
string | Name attribute for the hidden input containing cropped base64 data |
$options |
array | Configuration options (see below) |
Options:
| Option | Type | Default | Description |
|---|---|---|---|
id |
string | auto-generated | Unique widget identifier |
file_input_name |
string | {inputName}_file |
Name for the file input element |
aspect_ratio |
float | 1 |
Width/height ratio (1 = square, 16/9 = widescreen) |
crop_width |
int | 300 |
Output image width in pixels |
crop_height |
int | 300 |
Output image height in pixels |
max_size_mb |
int | 5 |
Maximum allowed file size in MB |
quality |
float | 0.8 |
JPEG compression quality (0.1 - 1.0) |
modal_id |
string | null |
Bootstrap modal ID for auto-reset on close |
labels |
array | [] |
Custom UI text labels |
Returns: Illuminate\Support\HtmlString
DragDropCrop::uploadFromBase64(?string $base64String, ?string $directory, ?string $namePrefix, ?string $existingPath)
Converts base64 image data to a file and saves it.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
$base64String |
string|null | - | Base64-encoded image data from the widget |
$directory |
string|null | config value | Upload directory relative to public/ |
$namePrefix |
string|null | null |
Prefix for the generated filename |
$existingPath |
string|null | null |
Path to existing image (will be deleted) |
Returns: string|null - Relative path to the uploaded file, or $existingPath if no new upload
Example with all parameters:
$path = DragDropCrop::uploadFromBase64( $request->input('profile_image'), // Base64 from form 'uploads/users', // Save to public/uploads/users/ 'user_' . $user->id, // Prefix: user_123_... $user->current_avatar // Delete old avatar );
Generated filename format: {prefix}_{timestamp}_{random}.{ext}
Example: user_123_1704067200_a1b2c3d4e5.jpg
Configuration
After publishing, edit config/drag-drop-crop.php:
return [ // Default upload directory (relative to public/) 'upload_directory' => 'frontend/auth', // Default aspect ratio (1 = square) 'aspect_ratio' => 1, // Default crop dimensions 'crop_width' => 300, 'crop_height' => 300, // Maximum file size in MB 'max_size_mb' => 5, // JPEG compression quality (0.1 - 1.0) 'quality' => 0.8, // Published assets path 'asset_path' => 'vendor/drag-drop-crop', ];
Complete Example
Blade View (create-profile.blade.php)
<form action="{{ route('profile.store') }}" method="POST"> @csrf <div class="mb-3"> <label class="form-label">Profile Picture</label> {!! DragDropCrop::generateUpload('avatar', [ 'aspect_ratio' => 1, 'crop_width' => 400, 'crop_height' => 400, 'max_size_mb' => 10, 'quality' => 0.9, 'labels' => [ 'title' => 'Drop your photo here', 'supports' => 'JPG, PNG, GIF (Max 10MB)', ], ]) !!} </div> <div class="mb-3"> <label class="form-label">Name</label> <input type="text" name="name" class="form-control"> </div> <button type="submit" class="btn btn-primary">Save Profile</button> </form>
Controller (ProfileController.php)
<?php namespace App\Http\Controllers; use App\Models\User; use Illuminate\Http\Request; use DragDropCropImageUpload\Facades\DragDropCrop; class ProfileController extends Controller { public function store(Request $request) { $request->validate([ 'name' => 'required|string|max:255', 'avatar' => 'nullable|string', // Base64 string ]); $avatarPath = null; if ($request->filled('avatar')) { $avatarPath = DragDropCrop::uploadFromBase64( $request->input('avatar'), 'uploads/avatars', 'avatar' ); } $user = User::create([ 'name' => $request->input('name'), 'avatar' => $avatarPath, ]); return redirect()->route('profile.show', $user) ->with('success', 'Profile created!'); } public function update(Request $request, User $user) { $request->validate([ 'name' => 'required|string|max:255', 'avatar' => 'nullable|string', ]); // This will delete the old avatar and upload the new one $avatarPath = DragDropCrop::uploadFromBase64( $request->input('avatar'), 'uploads/avatars', 'avatar_' . $user->id, $user->avatar // Existing path to delete ); $user->update([ 'name' => $request->input('name'), 'avatar' => $avatarPath, ]); return redirect()->route('profile.show', $user) ->with('success', 'Profile updated!'); } }
Display the Image
@if($user->avatar) <img src="{{ asset($user->avatar) }}" alt="Avatar" class="rounded-circle"> @endif
Supported Image Formats
| Format | Extension | MIME Type |
|---|---|---|
| JPEG | .jpg | image/jpeg |
| PNG | .png | image/png |
| GIF | .gif | image/gif |
| WebP | .webp | image/webp |
User Interaction Flow
- User drags an image onto the widget (or clicks to browse)
- Selected image appears in the cropping interface
- User adjusts the crop area (drag, resize, rotate)
- User clicks "Crop Image" to confirm
- Cropped preview is shown with option to change
- On form submit, base64 data is sent to the server
- Server converts base64 to file and returns the path
License
MIT License. See LICENSE for details.
Credits
- Cropper.js - Image cropping library
- Built for Laravel 11 & 12
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.