How to implement Dropzone.js In Laravel🚀
In this blog, we’ll learn how to integrate Dropzone.js — a modern, elegant drag-and-drop file uploader — into a Laravel application. We'll create a user registration form with name, email, and a file uploader as input fields. To handle file uploads, we’ll use Dropzone.js and allow uploading multiple files.
For demonstration purposes, I’ve used two tables: a users table and a user_images table. The user_images table will store multiple images for a single user.
Below is the table structure and relationship we’ll implement. To keep this blog short and crisp, I’m not including the migration details here.
📌 What is Dropzone.js?
Dropzone.js is a lightweight JavaScript library that provides drag-and-drop file uploads with image previews, progress bars, and customizable UI.
✅ Why Dropzone.js?
- Smooth drag-and-drop UI
- Auto image previews
- Built-in progress indicators
- Easy to integrate and customize
- Works well with Laravel backend
Step 1 - Setup Routes :
Route::get('/register', [UserRegistrationController::class, 'index']); Route::post('/register/upload', [UserRegistrationController::class, 'upload'])->name('register.upload'); Route::post('/register/store', [UserRegistrationController::class, 'register'])->name('register.store');
Step 2 - Controller Logic (UserRegistrationContoller.php) :
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\User; use App\Models\UserImage; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; use Illuminate\Support\Facades\Hash; class UserRegistrationController extends Controller { public function index() { return view('registerUser'); } public function upload(Request $request) { $files = []; if ($request->file && is_array($request->file)) { foreach ($request->file as $file) { $fileName = time() . '_' . rand() . '_' . $file->getClientOriginalName(); $file->move(public_path('uploads'), $fileName); $files[] = $fileName; // Store temporarily in session or in cache session()->push('uploaded_images', $fileName); } } return response()->json(['success' => true, 'files' => $files]); } public function register(Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users,email', 'uploaded_images' => 'required|array|min:1' ]); if ($validator->fails()) { return back()->withErrors($validator)->withInput(); } $randomPassword = Str::random(10); $user = User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($randomPassword), ]); foreach ($request->uploaded_images as $fileName) { UserImage::create([ 'user_id' => $user->id, 'file_name' => $fileName ]); } session()->forget('uploaded_images'); return redirect()->back()->with('success', 'User registered successfully! ✅'); } }
Step 2 - registerUser.blade.php :
<!DOCTYPE html> <html> <head> <title>User Registration + Dropzone File Upload</title> <meta name="csrf-token" content="{{ csrf_token() }}"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.css" /> <style> body { font-family: Arial, sans-serif; background: #f8fafc; padding: 40px; } .form-container { max-width: 600px; margin: auto; background: #ffffff; padding: 30px; border-radius: 10px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); } input, button { width: 100%; padding: 12px; margin: 10px 0; border-radius: 6px; border: 1px solid #ccc; } .dropzone { border: 2px dashed #6366f1; background: #eef2ff; padding: 30px; margin-top: 20px; } .dz-message { font-size: 16px; color: #4b5563; } </style> </head> <body> <div class="form-container"> <h2 style="text-align:center;">📝 User Registration</h2> @if (session('success')) <p style="color:green;">{{ session('success') }}</p> @endif @if ($errors->any()) <ul style="color:red;"> @foreach ($errors->all() as $error) <li>⚠️ {{ $error }}</li> @endforeach </ul> @endif <form id="mainForm" method="POST" action="{{ route('register.store') }}"> @csrf <input type="text" name="name" placeholder="👤 Full Name" value="{{ old('name') }}" required /> <input type="email" name="email" placeholder="📧 Email Address" value="{{ old('email') }}" required /> <input type="hidden" name="uploaded_images[]" id="uploaded_images" /> <div class="dropzone" id="dropzoneUploader"> <div class="dz-message">📤 Drop files here or click to upload</div> </div> <button type="submit">🚀 Submit Registration</button> </form> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script> <script> Dropzone.autoDiscover = false; let uploadedImages = []; const myDropzone = new Dropzone("#dropzoneUploader", { url: "{{ route('register.upload') }}", maxFilesize: 50, // MB uploadMultiple: true, parallelUploads: 10, acceptedFiles: ".jpg,.jpeg,.png,.gif,.pdf", addRemoveLinks: true, headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, success: function(file, response) { if (response && Array.isArray(response.files)) { file.customNames = []; response.files.forEach(fileName => { if (!uploadedImages.includes(fileName)) { uploadedImages.push(fileName); file.customNames.push(fileName); } }); } updateHiddenFields(); }, removedfile: function(file) { if (file.customNames && Array.isArray(file.customNames)) { file.customNames.forEach(name => { uploadedImages = uploadedImages.filter(img => img !== name); }); } updateHiddenFields(); file.previewElement.remove(); } }); function updateHiddenFields() { // Clear previous hidden inputs document.querySelectorAll('input[name="uploaded_images[]"]').forEach(el => el.remove()); const form = document.getElementById('mainForm'); uploadedImages.forEach(name => { const input = document.createElement('input'); input.type = "hidden"; input.name = "uploaded_images[]"; input.value = name; form.appendChild(input); }); } </script> </body> </html>
Output Demo:
🎯 Key Benefits of Dropzone.js Integration
🖼️ Drag & Drop with Previews
- Your users can upload multiple images easily with built-in previews.
⚡ Improved UX
- Visual progress indicators and modern UI make your form feel interactive.
🗃️ Separate Image Storage
- Storing image info in a separate table keeps the user model clean and scalable.
🧪 Validation Ready
- Laravel’s powerful validation ensures only correct data gets saved.
📱 Responsive & Mobile Friendly
- Dropzone can adapt to various screen sizes and device types.
🎨 Custom Theming
- You can style Dropzone easily using your own CSS or theming tools.