How to implement Drag & Dop feature using Dropzone.js - Laravel

 How to implement Dropzone.js In Laravel🚀

How to implement Drag & Dop feature using Dropzone.js - 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.

How to implement Drag & Dop feature using Dropzone.js - Laravel


📌 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.


Thank you for reading this article 😊

For any query do not hesitate to comment 💬



Previous Post Next Post

Contact Form