diff --git a/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.info.yml b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.info.yml
new file mode 100644
index 0000000..d571f67
--- /dev/null
+++ b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.info.yml
@@ -0,0 +1,9 @@
+name: 'Bulk Media Upload'
+core: 8.x
+type: module
+description: 'Provides a form to upload many media items at once.'
+version: '8.x-2.x-dev'
+package: Lightning
+dependencies:
+  - dropzonejs
+  - lightning_media
diff --git a/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.install b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.install
new file mode 100644
index 0000000..7544bac
--- /dev/null
+++ b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.install
@@ -0,0 +1,8 @@
+<?php
+
+/**
+ * Implements hook_install().
+ */
+function lightning_media_bulk_upload_install() {
+  user_role_grant_permissions('media_creator', ['dropzone upload files']);
+}
diff --git a/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.links.action.yml b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.links.action.yml
new file mode 100644
index 0000000..0b1638e
--- /dev/null
+++ b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.links.action.yml
@@ -0,0 +1,5 @@
+lightning_media.bulk_upload:
+  route_name: lightning_media.bulk_upload
+  title: 'Bulk upload'
+  appears_on:
+    - view.media.media_page_list
diff --git a/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.routing.yml b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.routing.yml
new file mode 100644
index 0000000..1733f7b
--- /dev/null
+++ b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/lightning_media_bulk_upload.routing.yml
@@ -0,0 +1,7 @@
+lightning_media.bulk_upload:
+  path: '/admin/content/media/bulk-upload'
+  defaults:
+    _title: 'Bulk upload'
+    _form: '\Drupal\lightning_media_bulk_upload\Form\BulkUploadForm'
+  requirements:
+    _permission: 'dropzone upload files,create media'
diff --git a/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/src/Form/BulkUploadForm.php b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/src/Form/BulkUploadForm.php
new file mode 100644
index 0000000..6364adb
--- /dev/null
+++ b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/src/Form/BulkUploadForm.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Drupal\lightning_media_bulk_upload\Form;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\lightning_core\Element;
+use Drupal\lightning_media\Exception\IndeterminateBundleException;
+use Drupal\lightning_media\MediaHelper;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * A form for uploading multiple media assets at once.
+ */
+class BulkUploadForm extends FormBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The media helper service.
+   *
+   * @var \Drupal\lightning_media\MediaHelper
+   */
+  protected $helper;
+
+  /**
+   * BulkUploadForm constructor.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\lightning_media\MediaHelper $helper
+   *   The media helper service.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translator
+   *   The string translation service.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaHelper $helper, TranslationInterface $translator) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->helper = $helper;
+    $this->setStringTranslation($translator);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('lightning.media_helper'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'bulk_upload_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $extensions = $this->helper->getFileExtensions(TRUE);
+
+    $form['dropzone'] = [
+      '#type' => 'dropzonejs',
+      '#dropzone_description' => $this->t('Drag files here to upload them'),
+      '#extensions' => implode(' ', $extensions),
+    ];
+    $form['continue'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Continue'),
+    ];
+
+    $variables = [
+      '@max_size' => static::bytesToString(file_upload_max_size()),
+      '@extensions' => Element::oxford($extensions),
+    ];
+    $form['dropzone']['#description'] = $this->t('You can upload as many files as you like. Each file can be up to @max_size in size. The following file extensions are accepted: @extensions', $variables);
+
+    return $form;
+  }
+
+  /**
+   * Converts a number of bytes into a human-readable string.
+   *
+   * @param int $bytes
+   *   A number of bytes.
+   *
+   * @return string
+   *   The human-readable measurement, like '2 MB' or '10 GB'.
+   */
+  public static function bytesToString($bytes) {
+    $units = array_map('t', ['bytes', 'KB', 'MB', 'GB', 'TB']);
+
+    while ($bytes > 1024) {
+      $bytes /= 1024;
+      array_shift($units);
+    }
+    return $bytes . ' ' . reset($units);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $bulk_create = [];
+
+    $uploads = $form_state->getValue(['dropzone', 'uploaded_files']);
+
+    foreach ($uploads as $upload) {
+      // Create a file entity for the temporary file.
+      /** @var \Drupal\file\FileInterface $file */
+      $file = $this->entityTypeManager->getStorage('file')->create([
+        'uri' => $upload['path'],
+      ]);
+      $file->setTemporary();
+      $file->save();
+
+      try {
+        $entity = $this->helper->createFromInput($file);
+      }
+      catch (IndeterminateBundleException $e) {
+        drupal_set_message('error', (string) $e);
+        continue;
+      }
+
+      $file = MediaHelper::useFile($entity, $file);
+      $file->setPermanent();
+      $file->save();
+      $entity->save();
+      array_push($bulk_create, $bulk_create ? $entity->id() : $entity);
+    }
+
+    if ($bulk_create) {
+      /** @var \Drupal\media_entity\MediaInterface $entity */
+      $redirect = array_shift($bulk_create)->toUrl('edit-form', [
+        'query' => [
+          'bulk_create' => $bulk_create,
+        ],
+      ]);
+      $form_state->setRedirectUrl($redirect);
+    }
+  }
+
+}
diff --git a/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/tests/features/bulk_upload.feature b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/tests/features/bulk_upload.feature
new file mode 100644
index 0000000..c460741
--- /dev/null
+++ b/modules/lightning_features/lightning_media/modules/lightning_media_bulk_upload/tests/features/bulk_upload.feature
@@ -0,0 +1,16 @@
+@lightning @api @media
+Feature: Bulk uploading media assets
+
+  @test_module @javascript @72286b5d
+  Scenario: Bulk uploading media assets
+    Given I am logged in as a user with the media_creator role
+    When I visit "/admin/content/media"
+    And I click "Bulk upload"
+    # Wait for Dropzone to be fully initialized.
+    And I wait 5 seconds
+    And I attach the file "test.jpg" to the dropzone
+    And I attach the file "test.pdf" to the dropzone
+    And I press "Continue"
+    And I press "Save and keep published"
+    And I press "Save and keep published"
+    Then I should be visiting a media entity
