diff --git a/media_bulk_upload.api.php b/media_bulk_upload.api.php
new file mode 100644
index 0000000..689fd54
--- /dev/null
+++ b/media_bulk_upload.api.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Media Bulk Upload module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Alter the results of a file_upload.
+ *
+ * Ensuring that the output of $file_ids is a list of file entities.
+ *
+ * @param array $file_ids
+ *   The file upload data. The value depends on the data structure of the
+ *   'file_upload' widget type. It's usually a list of file IDs.
+ * @param string $media_bulk_config_id
+ *   The Media Bulk Config entity ID.
+ * @param array $context
+ *   A collection of items which can be used to identify the context in which
+ *   a file upload is being done. The variable contains 4 keys:
+ *     - media_bulk_upload_form: the current MediaBulkUploadForm instance.
+ *     - form: the current form array.
+ *     - form_state: the current form state.
+ *     - media_bulk_config: the Media Bulk Config entity.
+ */
+function hook_media_bulk_upload_file_ids_alter(&$file_ids, string $media_bulk_config_id, array $context) {
+  $form = $context['form'];
+  if ($media_bulk_config_id === 'foo' && $form['file_upload']['#type'] === 'dropzonejs') {
+    // Handle 'foo' dropzonejs uploads in a different manner.
+    $file_ids = [1,2];
+  }
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/modules/dropzonejs/media_bulk_upload_dropzonejs.module b/modules/dropzonejs/media_bulk_upload_dropzonejs.module
index 9c9a64c..f2d703b 100644
--- a/modules/dropzonejs/media_bulk_upload_dropzonejs.module
+++ b/modules/dropzonejs/media_bulk_upload_dropzonejs.module
@@ -1,45 +1,48 @@
 <?php
 
-use Drupal\Core\Entity\EntityInterface;
+/**
+ * @file
+ * Contains Drupal hooks for the media_bulk_upload_dropzonejs.module.
+ */
+
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\file\FileInterface;
 
 /**
  * Implements hook_media_bulk_upload_file_ids_alter().
- *
- * @param array $file_ids
- * @param string $media_bulk_config_id
- *
- * @return void
  */
-function media_bulk_upload_dropzonejs_media_bulk_upload_file_ids_alter(array &$file_ids, string $media_bulk_config_id) {
-  if (!isset($file_ids['uploaded_files'])) {
+function media_bulk_upload_dropzonejs_media_bulk_upload_file_ids_alter(array &$file_ids, string $media_bulk_config_id, array $context) {
+  $form = $context['form'];
+  if (empty($file_ids['uploaded_files']) || $form['file_upload']['#type'] !== 'dropzonejs') {
     return;
   }
 
+  /** @var \Drupal\Core\File\FileSystemInterface $fileSystem */
+  $fileSystem = \Drupal::service('file_system');
+
+  /** @var \Drupal\dropzonejs\DropzoneJsUploadSaveInterface $dropzoneJsUploadSave */
+  $dropzoneJsUploadSave = \Drupal::service('dropzonejs.upload_save');
   $fileIds = [];
-  $fileStorage = \Drupal::entityTypeManager()->getStorage('file');
+
+  $currentUser = \Drupal::currentUser();
+  $extensions = $form['file_upload']['#extensions'] ?? '';
+  $validators = $form['file_upload']['#upload_validators'] ?? [];
 
   foreach ($file_ids['uploaded_files'] as $fileData) {
-    if (!is_array($fileData) || !isset($fileData['path'])) {
+    if (!is_array($fileData) || !isset($fileData['path']) || !file_exists($fileData['path'])) {
       continue;
     }
 
-    /** @var \Drupal\file\FileInterface $fileEntity */
-    $fileEntity = $fileStorage->create([
-      'uri' => $fileData['path'],
-      'uid' => \Drupal::currentUser()->id(),
-      'filename' => $fileData['filename'],
-    ]);
-    $fileEntity->setPermanent();
-    $fileEntity->save();
-    $fileIds[] = $fileEntity->id();
+    $destination = $fileSystem->dirname($fileData['path']);
+    $file = $dropzoneJsUploadSave->createFile($fileData['path'], $destination, $extensions, $currentUser, $validators);
+    if ($file) {
+      $file->setPermanent();
+      $file->save();
+      $fileIds[] = $file->id();
+    }
   }
 
-  if (!empty($fileIds)) {
-    $file_ids = $fileIds;
-  }
+  $file_ids = $fileIds;
 }
 
 /**
@@ -49,7 +52,6 @@ function media_bulk_upload_dropzonejs_form_media_bulk_upload_form_alter(
   &$form,
   FormStateInterface $form_state
 ) {
-
   $form['file_upload']['#type'] = 'dropzonejs';
   $form['file_upload']['#disable_form_buttons'] = '.button.form-submit';
   $form['file_upload']['#dropzone_description'] = new TranslatableMarkup('Click or drop your files here');
diff --git a/src/Form/MediaBulkUploadForm.php b/src/Form/MediaBulkUploadForm.php
index 8f2663e..5acbd3f 100644
--- a/src/Form/MediaBulkUploadForm.php
+++ b/src/Form/MediaBulkUploadForm.php
@@ -5,12 +5,16 @@ namespace Drupal\media_bulk_upload\Form;
 use Drupal;
 use Drupal\Component\Utility\Bytes;
 use Drupal\Component\Utility\Environment;
+use Drupal\Core\Entity\EntityConstraintViolationListInterface;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\File\FileExists;
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\Core\Utility\Error;
 use Drupal\file\Entity\File;
@@ -94,6 +98,13 @@ class MediaBulkUploadForm extends FormBase {
    */
   protected $fileRepository;
 
+  /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
   /**
    * The file validation service.
    *
@@ -118,7 +129,7 @@ class MediaBulkUploadForm extends FormBase {
    * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
    * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
    */
-  public function __construct(EntityTypeManagerInterface $entityTypeManager, MediaSubFormManager $mediaSubFormManager, AccountProxyInterface $currentUser, MessengerInterface $messenger, FileRepositoryInterface $fileRepository, FileValidatorInterface $validator) {
+  public function __construct(EntityTypeManagerInterface $entityTypeManager, MediaSubFormManager $mediaSubFormManager, AccountProxyInterface $currentUser, MessengerInterface $messenger, FileRepositoryInterface $fileRepository, FileValidatorInterface $validator, RendererInterface $renderer) {
     $this->mediaTypeStorage = $entityTypeManager->getStorage('media_type');
     $this->mediaBulkConfigStorage = $entityTypeManager->getStorage('media_bulk_config');
     $this->mediaStorage = $entityTypeManager->getStorage('media');
@@ -127,6 +138,7 @@ class MediaBulkUploadForm extends FormBase {
     $this->currentUser = $currentUser;
     $this->messenger = $messenger;
     $this->fileRepository = $fileRepository;
+    $this->renderer = $renderer;
     $this->maxFileSizeForm = '';
     $this->fileValidator = $validator;
   }
@@ -144,7 +156,8 @@ class MediaBulkUploadForm extends FormBase {
       $container->get('current_user'),
       $container->get('messenger'),
       $container->get('file.repository'),
-      $container->get('file.validator')
+      $container->get('file.validator'),
+      $container->get('renderer')
     );
   }
 
@@ -342,7 +355,13 @@ class MediaBulkUploadForm extends FormBase {
     $mediaBulkConfig = $this->mediaBulkConfigStorage->load($mediaBundleConfigId);
     $fileIds = $values['file_upload'];
 
-    \Drupal::moduleHandler()->alter('media_bulk_upload_file_ids', $fileIds, $mediaBundleConfigId);
+    $mediaBulkUploadContext = [
+      'media_bulk_upload_form' => $this,
+      'form' => $form,
+      'form_state' => $form_state,
+      'media_bulk_config' => $mediaBulkConfig,
+    ];
+    \Drupal::moduleHandler()->alter('media_bulk_upload_file_ids', $fileIds, $mediaBundleConfigId, $mediaBulkUploadContext);
 
     if (empty($fileIds)) {
       return;
@@ -407,9 +426,11 @@ class MediaBulkUploadForm extends FormBase {
     try {
       $media = $this->processFile($mediaBulkConfig, $file);
       if ($this->mediaSubFormManager->validateMediaFormDisplayUse($operation_details['media_bulk_config'])) {
+        /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $mediaFormDisplay */
         $extracted = $mediaFormDisplay->extractFormValues($media, $form['fields']['shared'], $form_state);
         $this->copyFormValuesToEntity($media, $extracted, $form_state);
       }
+      $this->validateMedia($media);
       $media->save();
       $context['results'][] = $id;
 
@@ -653,13 +674,60 @@ class MediaBulkUploadForm extends FormBase {
    *   Media Bulk Upload Form.
    */
   protected function prepareFormValues(FormStateInterface $form_state) {
+    $shared = (array) $form_state->getValue(['fields', 'shared']);
+    $shared_field_updated = FALSE;
+    // Check if we have entities that need to be created before starting the
+    // batch process.
+    foreach ($shared as $name => $value) {
+      if (empty($value['target_id']) || !is_array($value['target_id'])) {
+        continue;
+      }
+      foreach ($value['target_id'] as $delta => $item) {
+        if (empty($item['entity']) || !($item['entity'] instanceof EntityInterface)) {
+          continue;
+        }
+        $entity = $item['entity'];
+        if ($entity->isNew()) {
+          try {
+            // If it's a new entity, then attempt to reference the target
+            // ID instead to avoid accidental creation of duplicate.
+            unset($shared[$name]['target_id'][$delta]['entity']);
+            $shared_field_updated = TRUE;
+            if ($entity instanceof FieldableEntityInterface) {
+              $violations = $entity->validate();
+              $violations->filterByFieldAccess();
+              if ($violations->count()) {
+                throw new \RuntimeException(
+                  sprintf('Entity validation failed: %s', implode(' || ', $this->getViolationErrorMessages($violations)))
+                );
+              }
+            }
+            // No validation errors. Save, then use the entity target ID
+            // instead.
+            $entity->save();
+            $shared[$name]['target_id'][$delta]['target_id'] = $entity->id();
+          }
+          catch (\Exception $e) {
+            $args = [
+              '%field' => $name,
+            ];
+            watchdog_exception('media_bulk_upload', $e, 'Could not create entity for field %field.', $args);
+            $this->messenger()->addError($this->t('Failed to create entity for field %field.', $args), 'error');
+          }
+        }
+      }
+    }
+
     // If the shared name is empty, remove it from the form state.
     // Otherwise the extractFormValues function will override with an empty value.
-    $shared = $form_state->getValue(['fields', 'shared']);
     if (empty($shared['name'][0]['value'])) {
       unset($shared['name']);
+      $shared_field_updated = TRUE;
+    }
+    if ($shared_field_updated) {
       $form_state->setValue(['fields', 'shared'], $shared);
     }
+
     return $this;
   }
 
@@ -705,6 +773,66 @@ class MediaBulkUploadForm extends FormBase {
         }
       }
     }
+    return $errors;
+  }
+
+  /**
+   * Validate the Media entity.
+   *
+   * It deletes the underlying File entity if the media validation fails.
+   *
+   * @param \Drupal\media\MediaInterface $media
+   *   The media entity to validate.
+   *
+   * @throws \Drupal\Core\Entity\EntityStorageException
+   */
+  protected function validateMedia(MediaInterface $media) {
+    $violations = $media->validate();
+    $violations->filterByFieldAccess();
+    if ($violations->count()) {
+      $errors = $this->getViolationErrorMessages($violations);
+      $file_id = $media->getSource()->getSourceFieldValue($media);
+      if ($file_id && $file = $this->fileStorage->load($file_id)) {
+        // Delete the underlying file entity.
+        $file->delete();
+      }
+
+      $message = [
+        'error' => [
+          '#markup' => $this->t('The specified media %name could not be uploaded.', ['%name' => $media->label()]),
+        ],
+        'item_list' => [
+          '#theme' => 'item_list',
+          '#items' => $errors,
+        ],
+      ];
+      $this->messenger()->addError($this->renderer->renderPlain($message));
+      throw new \Exception(
+        sprintf('Media "%s" could not be created: %s', $media->label(), implode(' || ', $errors))
+      );
+    }
+  }
+
+  /**
+   * Returns the violation error messages as an array.
+   *
+   * @param \Drupal\Core\Entity\EntityConstraintViolationListInterface $violations
+   *   The entity violations.
+   *
+   * @return array
+   *   The list of entity violation messages.
+   */
+  protected function getViolationErrorMessages(EntityConstraintViolationListInterface $violations) {
+    $errors = [];
+    foreach ($violations as $violation) {
+      if ($violation->getPropertyPath()) {
+        $errors[] = sprintf('%s=%s', $violation->getPropertyPath(), $violation->getMessage());
+      }
+      else {
+        $errors[] = $violation->getMessage();
+      }
+    }
+    return $errors;
   }
 
 }
