PHPackages                             ethsam/symfony-dropzone - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. ethsam/symfony-dropzone

ActiveSymfony-bundle[File &amp; Storage](/categories/file-storage)

ethsam/symfony-dropzone
=======================

Symfony Form Type integrating Dropzone.js for drag-and-drop file uploads with entity relationship support

v2.0.0(2mo ago)12.3k2MITPHPPHP &gt;=8.1

Since Aug 22Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/ethsam/symfony-dropzone)[ Packagist](https://packagist.org/packages/ethsam/symfony-dropzone)[ Docs](https://github.com/ethsam/symfony-dropzone)[ RSS](/packages/ethsam-symfony-dropzone/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (10)Versions (3)Used By (0)

Symfony Dropzone Bundle
=======================

[](#symfony-dropzone-bundle)

[![Latest Version on Packagist](https://camo.githubusercontent.com/1e242ba70603bc62ff2f78d45202eb5481e89ab57567efcf4134d0d636d8cfb4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f65746873616d2f73796d666f6e792d64726f707a6f6e652e737667)](https://packagist.org/packages/ethsam/symfony-dropzone)[![License](https://camo.githubusercontent.com/947dfff4f2c870069a478a50fec3ce8c74e84e794e5f6c1682c89782fc231684/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f65746873616d2f73796d666f6e792d64726f707a6f6e652e737667)](LICENSE)[![PHP Version](https://camo.githubusercontent.com/356253124021e59e3cc492c765230918ea2be8a872e6f2a007ab573540ae13e6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f65746873616d2f73796d666f6e792d64726f707a6f6e652e737667)](composer.json)

> Seamless integration of Dropzone.js into Symfony Forms with automatic entity relationship management for drag-and-drop file uploads.

**[EN](README.md) | [FR](docs/README.fr.md) | [ES](docs/README.es.md)**

Features
--------

[](#features)

- **Drag-and-drop file uploads** — Powered by Dropzone.js
- **Entity relationship support** — Automatically manage OneToMany and ManyToOne associations
- **Built-in data transformation** — IDs to entities via Doctrine ORM
- **Pre-populated edit forms** — Show existing files in edit mode
- **Fully configurable** — Dropzone.js options exposed in form builder
- **Single or multiple files** — Control upload mode per form field
- **Custom upload/remove handlers** — Route-based endpoints with JSON responses
- **Image resizing** — Client-side image processing before upload
- **Flexible authentication** — Custom headers for API integration
- **Symfony Flex compatible** — Automatic bundle registration

Requirements
------------

[](#requirements)

- **PHP**: ≥8.1
- **Symfony**: 5.4, 6.x, 7.x
- **Doctrine ORM**: 2.12+
- **Dropzone.js**: 6.0+ (included via CDN)

Installation
------------

[](#installation)

### Step 1: Install via Composer

[](#step-1-install-via-composer)

```
composer require ethsam/symfony-dropzone
```

The bundle registers automatically with Symfony Flex. If you're not using Flex, add to `config/bundles.php`:

```
Ethsam\SymfonyDropzone\SymfonyDropzoneBundle::class => ['all' => true],
```

### Step 2: Include Dropzone.js

[](#step-2-include-dropzonejs)

Add the following to your base template (e.g., `base.html.twig`):

```

```

That's it! You're ready to use `DropzoneType` in your forms.

Quick Start
-----------

[](#quick-start)

### 1. Define Your File Entity

[](#1-define-your-file-entity)

```
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Attachment
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    private string $filename = '';

    #[ORM\Column(length: 255)]
    private string $src = ''; // URL or path to file

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getFilename(): string
    {
        return $this->filename;
    }

    public function setFilename(string $filename): self
    {
        $this->filename = $filename;
        return $this;
    }

    public function getSrc(): string
    {
        return $this->src;
    }

    public function setSrc(string $src): self
    {
        $this->src = $src;
        return $this;
    }
}
```

### 2. Define Your Main Entity with Relationship

[](#2-define-your-main-entity-with-relationship)

```
namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Post
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    private string $title = '';

    // OneToMany relationship
    #[ORM\OneToMany(targetEntity: Attachment::class, mappedBy: 'post', cascade: ['persist', 'remove'])]
    private Collection $attachments;

    public function __construct()
    {
        $this->attachments = new ArrayCollection();
    }

    public function addAttachment(Attachment $attachment): self
    {
        if (!$this->attachments->contains($attachment)) {
            $this->attachments->add($attachment);
        }
        return $this;
    }

    public function removeAttachment(Attachment $attachment): self
    {
        $this->attachments->removeElement($attachment);
        return $this;
    }

    public function getAttachments(): Collection
    {
        return $this->attachments;
    }
}
```

### 3. Create a Form Type

[](#3-create-a-form-type)

```
namespace App\Form;

use App\Entity\Attachment;
use App\Entity\Post;
use Ethsam\SymfonyDropzone\Form\DropzoneType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PostFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('title', TextType::class, [
                'label' => 'Post Title',
            ])
            ->add('attachments', DropzoneType::class, [
                'class' => Attachment::class,
                'maxFiles' => 5,
                'multiple' => true,
                'uploadHandler' => 'app_upload_file',
                'removeHandler' => 'app_remove_file',
                'acceptedFiles' => 'image/*,.pdf',
                'addRemoveLinks' => true,
            ]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Post::class,
        ]);
    }
}
```

### 4. Create Upload/Remove Handlers

[](#4-create-uploadremove-handlers)

```
namespace App\Controller;

use App\Entity\Attachment;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;

class FileController extends AbstractController
{
    #[Route('/upload', name: 'app_upload_file', methods: ['POST'])]
    public function upload(Request $request, EntityManagerInterface $em): JsonResponse
    {
        $uploadedFile = $request->files->get('file');

        if (!$uploadedFile) {
            return new JsonResponse(['error' => 'No file provided'], 400);
        }

        // Move the file to your uploads directory
        $filename = uniqid() . '.' . $uploadedFile->guessExtension();
        $uploadedFile->move(
            $this->getParameter('kernel.project_dir') . '/public/uploads',
            $filename
        );

        // Create and persist the attachment
        $attachment = new Attachment();
        $attachment->setFilename($uploadedFile->getClientOriginalName());
        $attachment->setSrc('/uploads/' . $filename);

        $em->persist($attachment);
        $em->flush();

        return new JsonResponse(['id' => $attachment->getId()]);
    }

    #[Route('/remove/{id}', name: 'app_remove_file', methods: ['DELETE'])]
    public function remove(Attachment $attachment, EntityManagerInterface $em): JsonResponse
    {
        $id = $attachment->getId();

        // Optionally delete the file from disk
        // unlink($this->getParameter('kernel.project_dir') . '/public' . $attachment->getSrc());

        $em->remove($attachment);
        $em->flush();

        return new JsonResponse(['id' => $id]);
    }
}
```

### 5. Use the Form in Your Template

[](#5-use-the-form-in-your-template)

```
{# templates/post/create.html.twig #}
{% extends 'base.html.twig' %}

{% block content %}
    Create Post

    {{ form_start(form) }}
        {{ form_widget(form.title) }}
        {{ form_widget(form.attachments) }}
        Create
    {{ form_end(form) }}
{% endblock %}
```

That's it! The bundle handles everything:

- Dropzone.js widget rendering
- File upload via AJAX
- File ID storage in hidden fields
- Entity relationship transformation on form submission

Configuration Options
---------------------

[](#configuration-options)

OptionTypeDefaultDescription`class`stringnull**Required.** Entity class for file/attachment objects`multiple`booltrueEnable multiple file mode; set `false` for single file (ManyToOne)`maxFiles`int1Maximum number of files allowed in the dropzone`uploadHandler`stringnull**Required.** Symfony route name for file upload endpoint`removeHandler`stringnull**Required.** Symfony route name for file removal endpoint`uploadHandlerMethod`string"POST"HTTP method for upload requests`removeHandlerMethod`string"DELETE"HTTP method for remove requests`choice_src`string"src"Entity property name containing file URL/path (getter method: `get{PropertyName}()`)`acceptedFiles`stringnullMIME types accepted (e.g., `"image/*,.pdf"`)`addRemoveLinks`booltrueShow "Remove" link on file previews`headers`array\[\]Custom HTTP headers sent with requests (e.g., `['Authorization' => 'Bearer TOKEN']`)`formData`array\[\]Additional form data sent with upload request`withCredentials`int0XHR `withCredentials` setting (0 or 1)`thumbnailWidth`int120Width of preview thumbnails in pixels`thumbnailHeight`int120Height of preview thumbnails in pixels`thumbnailMethod`string"crop"Thumbnail scaling: `"crop"` or `"contain"``resizeWidth`intnullClient-side resize width before upload (preserves aspect ratio if only one set)`resizeHeight`intnullClient-side resize height before upload`resizeMimeType`stringnullOutput MIME type after resize (e.g., `"image/jpeg"`)`resizeMethod`string"contain"Resize scaling: `"crop"` or `"contain"``filesizeBase`int1024Base unit for filesize calculations`ignoreHiddenFiles`booltrueIgnore hidden files in directories`autoProcessQueue`booltrueAuto-process upload queue on file addition`autoQueue`booltrueAuto-queue files when added to dropzone`previewsContainer`stringnullCSS selector for custom preview container (e.g., `"#my-previews"`)`required`booltrueField is required for form validationFile Entity Requirements
------------------------

[](#file-entity-requirements)

Your file/attachment entity must implement:

- **`getId(): ?int`** — Returns the unique identifier
- **`getFilename(): string`** — Returns the filename for display
- **Getter for `choice_src` property** — By default `getSrc(): string`, returns the file URL/path for thumbnail display

Example minimal entity:

```
#[ORM\Entity]
class Attachment
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    private string $filename = '';

    #[ORM\Column(length: 255)]
    private string $src = '';

    public function getId(): ?int { return $this->id; }
    public function getFilename(): string { return $this->filename; }
    public function setFilename(string $filename): self { $this->filename = $filename; return $this; }
    public function getSrc(): string { return $this->src; }
    public function setSrc(string $src): self { $this->src = $src; return $this; }
}
```

How It Works
------------

[](#how-it-works)

### Architecture Overview

[](#architecture-overview)

1. **Form Type Registration** — `DropzoneType` extends Symfony's form system
2. **Hidden Fields** — For multiple files: a `CollectionType` with hidden inputs; for single: an `EntityType` field
3. **Twig Template** — Renders Dropzone.js widget with JavaScript configuration
4. **Upload Flow**:
    - User drags files or clicks to select
    - Dropzone.js sends each file to your `uploadHandler` route via AJAX
    - Handler persists entity to database, returns `{"id": }`
    - Bundle stores file ID in hidden form field
5. **Form Submission** — Hidden field values are collected
6. **Data Transformation** — `DropzoneTransformer` converts IDs back to entity objects via Doctrine
7. **Persistence** — Form submission handles OneToMany/ManyToOne relationships automatically

### File Removal Flow

[](#file-removal-flow)

1. User clicks "Remove" link on file preview
2. Dropzone.js sends DELETE (or POST) to `removeHandler` route
3. Handler deletes entity, returns `{"id": }`
4. Widget removes preview from DOM
5. On next form submission, removed ID is not included, relationship is updated

Examples
--------

[](#examples)

### Basic Multiple File Upload

[](#basic-multiple-file-upload)

```
$builder->add('attachments', DropzoneType::class, [
    'class' => Attachment::class,
    'multiple' => true,
    'maxFiles' => 10,
    'uploadHandler' => 'app_upload_file',
    'removeHandler' => 'app_remove_file',
]);
```

### Single File Upload (ManyToOne)

[](#single-file-upload-manytoone)

```
$builder->add('profileImage', DropzoneType::class, [
    'class' => ProfileImage::class,
    'multiple' => false,  // Single file mode
    'maxFiles' => 1,
    'uploadHandler' => 'app_upload_image',
    'removeHandler' => 'app_remove_image',
]);
```

### Image-Only with Custom Dimensions

[](#image-only-with-custom-dimensions)

```
$builder->add('photos', DropzoneType::class, [
    'class' => Photo::class,
    'acceptedFiles' => 'image/*',
    'maxFiles' => 5,
    'uploadHandler' => 'app_upload_photo',
    'removeHandler' => 'app_remove_photo',
    'thumbnailWidth' => 200,
    'thumbnailHeight' => 200,
    'thumbnailMethod' => 'contain',
    'resizeWidth' => 1920,
    'resizeHeight' => 1080,
    'resizeMethod' => 'contain',
    'resizeMimeType' => 'image/jpeg',
]);
```

### With Custom Headers (API Authentication)

[](#with-custom-headers-api-authentication)

```
$builder->add('documents', DropzoneType::class, [
    'class' => Document::class,
    'uploadHandler' => 'api_upload_document',
    'removeHandler' => 'api_remove_document',
    'headers' => [
        'Authorization' => 'Bearer ' . $this->apiToken,
    ],
    'formData' => [
        'documentType' => 'invoice',
    ],
]);
```

### Custom Preview Container

[](#custom-preview-container)

```
{# In template #}

{{ form_start(form) }}
    {{ form_widget(form.documents) }}
{{ form_end(form) }}

{# In form builder #}
$builder->add('documents', DropzoneType::class, [
    'class' => Document::class,
    'uploadHandler' => 'app_upload_document',
    'removeHandler' => 'app_remove_document',
    'previewsContainer' => '#my-previews',
]);
```

### With Custom Form Data

[](#with-custom-form-data)

```
$builder->add('uploads', DropzoneType::class, [
    'class' => Upload::class,
    'uploadHandler' => 'app_upload_file',
    'removeHandler' => 'app_remove_file',
    'formData' => [
        'category' => 'documents',
        'userId' => $this->currentUser->getId(),
    ],
]);
```

Your upload handler receives this in `$request->request->all()`:

```
public function upload(Request $request, EntityManagerInterface $em): JsonResponse
{
    $category = $request->request->get('category'); // 'documents'
    $userId = $request->request->get('userId');
    $file = $request->files->get('file');
    // ... handle upload
}
```

Upgrading from v1
-----------------

[](#upgrading-from-v1)

If you're upgrading from the original `emr-dev/symfony-dropzone`:

- **Bundle namespace changed** — `Ethsam\SymfonyDropzone` (was `EmrDev\SymfonyDropzoneBundle`)
- **Form type import** — Update: `use Ethsam\SymfonyDropzone\Form\DropzoneType;`
- **Option names** — No changes; all options are backward compatible
- **PHP requirement** — Now requires PHP ≥8.1
- **Symfony support** — Now supports Symfony 5.4, 6.x, 7.x
- **Data transformer** — Automatic; no manual entity conversion needed

Migration example:

```
// Before (v1)
use EmrDev\SymfonyDropzoneBundle\Form\DropzoneType;

// After (v2)
use Ethsam\SymfonyDropzone\Form\DropzoneType;
```

The API and functionality remain the same.

Differences from symfony/ux-dropzone
------------------------------------

[](#differences-from-symfonyux-dropzone)

Featureethsam/symfony-dropzonesymfony/ux-dropzone**Entity relationships**Full OneToMany/ManyToOne supportNone; form values only**Data transformation**Automatic ID → EntityManual**Multiple files**Built-in with CollectionTypeNot ideal**Edit mode pre-population**Yes; shows existing filesManual templating**Upload handler**Simple route + JSON responseRequires UX component**File removal**Built-in DELETE handlerManual**Dropzone config**Full access to all optionsLimited**Learning curve**Minimal; standard Symfony formsModerate; UX paradigm**Maintenance**ActiveOfficial but UX-focused**Summary**: Use `ethsam/symfony-dropzone` for entity-driven file management; use `symfony/ux-dropzone` if you need tight Stimulus integration or prefer the UX paradigm.

Contributing
------------

[](#contributing)

We welcome contributions! Please:

1. Fork the repository
2. Create a feature branch: `git checkout -b feat/your-feature`
3. Commit your changes: `git commit -m "feat: add your feature"`
4. Push to the branch: `git push origin feat/your-feature`
5. Open a Pull Request

For bug reports or feature requests, please [open an issue](https://github.com/ethsam/symfony-dropzone/issues).

License
-------

[](#license)

This bundle is licensed under the MIT License. See [LICENSE](LICENSE) for details.

Originally forked from [emr-dev/symfony-dropzone](https://github.com/emr-dev/symfony-dropzone).

Credits
-------

[](#credits)

- **Samuel Etheve** — Current maintainer
- **Emomaliev M.** — Original author ([emr-dev/symfony-dropzone](https://github.com/emr-dev/symfony-dropzone))
- **[Dropzone.js](https://www.dropzonejs.com/)** — File upload library

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance86

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1341 days

Total

2

Last Release

69d ago

Major Versions

v1.0.0 → v2.0.02026-04-25

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/27362741?v=4)[Samuel Ethève](/maintainers/ethsam)[@ethsam](https://github.com/ethsam)

---

Top Contributors

[![ethsam](https://avatars.githubusercontent.com/u/27362741?v=4)](https://github.com/ethsam "ethsam (2 commits)")

---

Tags

symfonyuploadformdrag-and-dropfile-uploaddropzone

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ethsam-symfony-dropzone/health.svg)

```
[![Health](https://phpackages.com/badges/ethsam-symfony-dropzone/health.svg)](https://phpackages.com/packages/ethsam-symfony-dropzone)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M386](/packages/easycorp-easyadmin-bundle)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1616.4k14](/packages/2lenet-crudit-bundle)[open-dxp/opendxp

Content &amp; Product Management Framework (CMS/PIM)

9421.6k61](/packages/open-dxp-opendxp)[chameleon-system/chameleon-base

The Chameleon System core.

1028.6k5](/packages/chameleon-system-chameleon-base)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
