diff --git a/config/install/system.action.message_delete_action.yml b/config/install/system.action.message_delete_action.yml
new file mode 100644
index 0000000..9834d5f
--- /dev/null
+++ b/config/install/system.action.message_delete_action.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - contact
+id: message_delete_action
+label: 'Delete message'
+type: contact_message
+plugin: message_delete_action
+configuration: {  }
diff --git a/config/install/views.view.contact_messages.yml b/config/install/views.view.contact_messages.yml
index 8250ad1..f2b7499 100644
--- a/config/install/views.view.contact_messages.yml
+++ b/config/install/views.view.contact_messages.yml
@@ -84,6 +84,59 @@ display:
       row:
         type: 'entity:contact_message'
       fields:
+        message_bulk_form:
+          id: message_bulk_form
+          table: contact_message
+          field: message_bulk_form
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ''
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          action_title: 'With selection'
+          include_exclude: exclude
+          selected_actions: {  }
+          entity_type: contact_message
+          plugin_id: message_bulk_form
         subject:
           id: subject
           table: contact_message
@@ -445,10 +498,14 @@ display:
       display_extenders: {  }
     cache_metadata:
       contexts:
-        0: user
-        2: cache.context.url
-        3: language
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - user.permissions
       cacheable: false
+      max-age: 0
+      tags: {  }
   page_1:
     display_plugin: page
     id: page_1
@@ -467,7 +524,11 @@ display:
       display_extenders: {  }
     cache_metadata:
       contexts:
-        0: user
-        2: cache.context.url
-        3: language
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - user.permissions
       cacheable: false
+      max-age: 0
+      tags: {  }
diff --git a/config/schema/contact_storage.schema.yml b/config/schema/contact_storage.schema.yml
index 92792ba..8f9c5c6 100644
--- a/config/schema/contact_storage.schema.yml
+++ b/config/schema/contact_storage.schema.yml
@@ -11,3 +11,7 @@ contact.form.*.third_party.contact_storage:
     show_preview:
       type: boolean
       label: 'Show preview button'
+
+action.configuration.message_delete_action:
+  type: action_configuration_default
+  label: 'Delete message configuration'
diff --git a/config/schema/contact_storage.views.schema.yml b/config/schema/contact_storage.views.schema.yml
index b000275..1ea1cce 100644
--- a/config/schema/contact_storage.views.schema.yml
+++ b/config/schema/contact_storage.views.schema.yml
@@ -1,3 +1,7 @@
 views.field.contact_form:
   type: views_field
   label: 'Contact form'
+
+views.field.message_bulk_form:
+  type: views_field_bulk_form
+  label: 'Contact Message bulk form'
diff --git a/contact_storage.routing.yml b/contact_storage.routing.yml
new file mode 100644
index 0000000..0363297
--- /dev/null
+++ b/contact_storage.routing.yml
@@ -0,0 +1,6 @@
+contact.multiple_delete_confirm:
+  path: '/admin/structure/contact/messages/delete'
+  defaults:
+    _form: '\Drupal\contact_storage\Form\DeleteMultiple'
+  requirements:
+    _permission: 'administer contact forms'
diff --git a/src/Form/DeleteMultiple.php b/src/Form/DeleteMultiple.php
new file mode 100644
index 0000000..7922e60
--- /dev/null
+++ b/src/Form/DeleteMultiple.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace Drupal\contact_storage\Form;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\user\PrivateTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Provides a message deletion confirmation form.
+ */
+class DeleteMultiple extends ConfirmFormBase {
+
+  /**
+   * The array of messages to delete.
+   *
+   * @var string[][]
+   */
+  protected $messageInfo = array();
+
+  /**
+   * The tempstore factory.
+   *
+   * @var \Drupal\user\PrivateTempStoreFactory
+   */
+  protected $tempStoreFactory;
+
+  /**
+   * The message storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $manager;
+
+  /**
+   * Constructs a DeleteMultiple form object.
+   *
+   * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
+   *   The tempstore factory.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $manager
+   *   The entity manager.
+   */
+  public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $manager) {
+    $this->tempStoreFactory = $temp_store_factory;
+    $this->storage = $manager->getStorage('contact_message');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.private_tempstore'),
+      $container->get('entity.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'message_multiple_delete_confirm';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->formatPlural(count($this->messageInfo), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('entity.contact_form.collection');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $this->messageInfo = $this->tempStoreFactory->get('message_multiple_delete_confirm')->get(\Drupal::currentUser()->id());
+    if (empty($this->messageInfo)) {
+      return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
+    }
+    /** @var \Drupal\contact\MessageInterface[] $messages[] */
+    $messages = $this->storage->loadMultiple(array_keys($this->messageInfo));
+
+    $items = [];
+    foreach ($this->messageInfo as $id => $langcodes) {
+      foreach ($langcodes as $langcode) {
+        $message = $messages[$id]->getTranslation($langcode);
+        $key = $id . ':' . $langcode;
+        $default_key = $id . ':' . $message->getUntranslated()->language()->getId();
+
+        // If we have a translated entity we build a nested list of translations
+        // that will be deleted.
+        $languages = $message->getTranslationLanguages();
+        if (count($languages) > 1 && $message->isDefaultTranslation()) {
+          $names = [];
+          foreach ($languages as $translation_langcode => $language) {
+            $names[] = $language->getName();
+            unset($items[$id . ':' . $translation_langcode]);
+          }
+          $items[$default_key] = [
+            'label' => [
+              '#markup' => $this->t('@label (Original translation) - <em>The following message translations will be deleted:</em>', ['@label' => $message->label()]),
+            ],
+            'deleted_translations' => [
+              '#theme' => 'item_list',
+              '#items' => $names,
+            ],
+          ];
+        }
+        elseif (!isset($items[$default_key])) {
+          $items[$key] = $message->label();
+        }
+      }
+    }
+
+    $form['messages'] = array(
+      '#theme' => 'item_list',
+      '#items' => $items,
+    );
+    $form = parent::buildForm($form, $form_state);
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state->getValue('confirm') && !empty($this->messageInfo)) {
+      $total_count = 0;
+      $delete_messages = [];
+      /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
+      $delete_translations = [];
+      /** @var \Drupal\contact\MessageInterface[] $messages[] */
+      $messages = $this->storage->loadMultiple(array_keys($this->messageInfo));
+
+      foreach ($this->messageInfo as $id => $langcodes) {
+        foreach ($langcodes as $langcode) {
+          $message = $messages[$id]->getTranslation($langcode);
+          if ($message->isDefaultTranslation()) {
+            $delete_messages[$id] = $message;
+            unset($delete_translations[$id]);
+            $total_count += count($message->getTranslationLanguages());
+          }
+          elseif (!isset($delete_messages[$id])) {
+            $delete_translations[$id][] = $message;
+          }
+        }
+      }
+
+      if ($delete_messages) {
+        $this->storage->delete($delete_messages);
+        $this->logger('content')->notice('Deleted @count messages.', array('@count' => count($delete_messages)));
+      }
+
+      if ($delete_translations) {
+        $count = 0;
+        foreach ($delete_translations as $id => $translations) {
+          $message = $messages[$id]->getUntranslated();
+          foreach ($translations as $translation) {
+            $message->removeTranslation($translation->language()->getId());
+          }
+          $message->save();
+          $count += count($translations);
+        }
+        if ($count) {
+          $total_count += $count;
+          $this->logger('content')->notice('Deleted @count message translations.', array('@count' => $count));
+        }
+      }
+
+      if ($total_count) {
+        drupal_set_message($this->formatPlural($total_count, 'Deleted 1 message.', 'Deleted @count messages.'));
+      }
+
+      $this->tempStoreFactory->get('message_multiple_delete_confirm')->delete(\Drupal::currentUser()->id());
+    }
+
+    $form_state->setRedirect('system.admin_content');
+  }
+
+}
diff --git a/src/MessageViewsData.php b/src/MessageViewsData.php
index 3af8031..6ff6503 100644
--- a/src/MessageViewsData.php
+++ b/src/MessageViewsData.php
@@ -28,6 +28,14 @@ class MessageViewsData extends EntityViewsData {
       ),
     );
 
+    $data['contact_message']['message_bulk_form'] = array(
+      'title' => $this->t('Message operations bulk form'),
+      'help' => $this->t('Add a form element that lets you run operations on multiple messages.'),
+      'field' => array(
+        'id' => 'message_bulk_form',
+      ),
+    );
+
     return $data;
   }
 
diff --git a/src/Plugin/Action/DeleteMessage.php b/src/Plugin/Action/DeleteMessage.php
new file mode 100644
index 0000000..3823189
--- /dev/null
+++ b/src/Plugin/Action/DeleteMessage.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\contact_storage\Plugin\Action;
+
+use Drupal\Core\Action\ActionBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\PrivateTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Redirects to a message deletion form.
+ *
+ * @Action(
+ *   id = "message_delete_action",
+ *   label = @Translation("Delete message"),
+ *   type = "contact_message",
+ *   confirm_form_route_name = "contact.multiple_delete_confirm"
+ * )
+ */
+class DeleteMessage extends ActionBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The tempstore object.
+   *
+   * @var \Drupal\user\SharedTempStore
+   */
+  protected $tempStore;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a new DeleteMessage object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
+   *   The tempstore factory.
+   * @param AccountInterface $current_user
+   *   Current user.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
+    $this->currentUser = $current_user;
+    $this->tempStore = $temp_store_factory->get('message_multiple_delete_confirm');
+
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('user.private_tempstore'),
+      $container->get('current_user')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function executeMultiple(array $entities) {
+    $info = [];
+    /** @var \Drupal\contact\MessageInterface $message */
+    foreach ($entities as $message) {
+      $langcode = $message->language()->getId();
+      $info[$message->id()][$langcode] = $langcode;
+    }
+    $this->tempStore->set($this->currentUser->id(), $info);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute($object = NULL) {
+    $this->executeMultiple(array($object));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    /** @var \Drupal\contact\MessageInterface $object */
+    return $object->access('delete', $account, $return_as_object);
+  }
+
+}
diff --git a/src/Plugin/views/field/MessageBulkForm.php b/src/Plugin/views/field/MessageBulkForm.php
new file mode 100644
index 0000000..4afee92
--- /dev/null
+++ b/src/Plugin/views/field/MessageBulkForm.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\contact_storage\Plugin\views\field;
+
+use Drupal\system\Plugin\views\field\BulkForm;
+
+/**
+ * Defines a contact message operations bulk form element.
+ *
+ * @ViewsField("message_bulk_form")
+ */
+class MessageBulkForm extends BulkForm {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function emptySelectedMessage() {
+    return $this->t('No message selected.');
+  }
+
+}
