diff --git a/diff.views.inc b/diff.views.inc
new file mode 100644
index 0000000..25ecec4
--- /dev/null
+++ b/diff.views.inc
@@ -0,0 +1,36 @@
+<?php
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Implements hook_views_data().
+ */
+function diff_views_data() {
+  $data = [];
+  $entity_types = \Drupal::entityTypeManager()->getDefinitions();
+  $revisionable_entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
+    return $entity_type->isRevisionable();
+  });
+
+  /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
+  foreach ($revisionable_entity_types as $entity_type) {
+    $revision_base_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable();
+
+    $data[$revision_base_table]['diff_from'] = [
+      'title' => t('Diff from'),
+      'help' => '',
+      'field' => [
+        'id' => 'diff__from',
+      ],
+    ];
+
+    $data[$revision_base_table]['diff_to'] = [
+      'title' => t('Diff to'),
+      'help' => '',
+      'field' => [
+        'id' => 'diff__to',
+      ],
+    ];
+  }
+
+  return $data;
+}
diff --git a/src/Plugin/views/field/DiffFrom.php b/src/Plugin/views/field/DiffFrom.php
new file mode 100644
index 0000000..c72ecd0
--- /dev/null
+++ b/src/Plugin/views/field/DiffFrom.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\diff\Plugin\views\field\DiffFrom.
+ */
+
+namespace Drupal\diff\Plugin\views\field;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Routing\RedirectDestinationTrait;
+use Drupal\node\NodeInterface;
+use Drupal\views\Plugin\views\field\FieldPluginBase;
+
+/**
+ * @ViewsField("diff__from")
+ */
+class DiffFrom extends DiffPluginBase {
+
+  use RedirectDestinationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+
+    $options['label']['default'] = t('From');
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewsForm(&$form, FormStateInterface $form_state) {
+    // Replace the form submit button label.
+    $form['actions']['submit']['#value'] = $this->t('Compare');
+
+    $use_revision = array_key_exists('revision', $this->view->getQuery()->getEntityTableInfo());
+    if (!empty($this->view->result)) {
+      $form[$this->options['id']]['#tree'] = TRUE;
+      foreach ($this->view->result as $row_index => $row) {
+        $entity = $row->_entity;
+        $form[$this->options['id']][$row_index] = [
+          '#type' => 'radio',
+          '#parents' => [$this->options['id']],
+          '#title' => $this->t('Compare this item'),
+          '#title_display' => 'invisible',
+          '#return_value' => $this->calculateEntityBulkFormKey($entity, $use_revision),
+        ];
+      }
+    }
+  }
+
+  protected function getToFieldId() {
+    $to_fields = array_filter($this->view->field, function (FieldPluginBase $field) {
+      return $field instanceof DiffTo;
+    });
+    return array_keys($to_fields)[0];
+  }
+
+  /**
+   * Submit handler for the bulk form.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   *   Thrown when the user tried to access an action without access to it.
+   */
+  public function viewsFormSubmit(&$form, FormStateInterface $form_state) {
+    if ($form_state->get('step') == 'views_form_views_form') {
+      $diff_from = $form_state->getValue($this->options['id']);
+      $diff_from_entity = $this->loadEntityFromBulkFormKey($diff_from);
+
+      $diff_to = $form_state->getValue($this->getToFieldId());
+      $diff_to_entity = $this->loadEntityFromBulkFormKey($diff_to);
+
+      $options = array(
+        'query' => $this->getDestinationArray(),
+      );
+      $entity_type_id = $diff_from_entity->getEntityTypeId();
+
+
+      if ($diff_from_entity instanceof NodeInterface) {
+        $form_state->setRedirect('diff.revisions_diff', [$entity_type_id => $diff_from_entity->id(),'left_vid' => $diff_from_entity->getRevisionId(), 'right_vid' => $diff_to_entity->getRevisionId()], $options);
+      }
+      else {
+        $route_name = 'entity.' . $entity_type_id . '.revisions_diff';
+        $form_state->setRedirect($route_name, [$entity_type_id => $diff_from_entity->id(), 'left_revision' => $diff_from_entity->getRevisionId(), 'right_revision' => $diff_to_entity->getRevisionId()], $options);
+      }
+    }
+  }
+
+
+}
diff --git a/src/Plugin/views/field/DiffPluginBase.php b/src/Plugin/views/field/DiffPluginBase.php
new file mode 100644
index 0000000..b938050
--- /dev/null
+++ b/src/Plugin/views/field/DiffPluginBase.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\diff\Plugin\views\field\DiffPluginBase.
+ */
+
+namespace Drupal\diff\Plugin\views\field;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\RevisionableInterface;
+use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\views\Plugin\views\field\FieldPluginBase;
+use Drupal\views\Plugin\views\field\UncacheableFieldHandlerTrait;
+use Drupal\views\ResultRow;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class DiffPluginBase extends FieldPluginBase {
+
+  use UncacheableFieldHandlerTrait;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new BulkForm 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\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity manager.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    $this->ensureMyTable();
+  }
+
+  public function getCacheMaxAge() {
+    return 0;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue(ResultRow $row, $field = NULL) {
+    return '<!--form-item-' . $this->options['id'] . '--' . $row->index . '-->';
+  }
+
+  /**
+   * Calculates a bulk form key.
+   *
+   * This generates a key that is used as the checkbox return value when
+   * submitting a bulk form. This key allows the entity for the row to be loaded
+   * totally independently of the executed view row.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to calculate a bulk form key for.
+   *
+   * @return string
+   *   The bulk form key representing the entity's id, language and revision (if
+   *   applicable) as one string.
+   *
+   * @see self::loadEntityFromBulkFormKey()
+   */
+  protected function calculateEntityBulkFormKey(EntityInterface $entity) {
+    $key_parts = [$entity->language()->getId(), $entity->id()];
+
+    if ($entity instanceof RevisionableInterface) {
+      $key_parts[] = $entity->getRevisionId();
+    }
+
+    // An entity ID could be an arbitrary string (although they are typically
+    // numeric). JSON then Base64 encoding ensures the bulk_form_key is
+    // safe to use in HTML, and that the key parts can be retrieved.
+    $key = json_encode($key_parts);
+    return base64_encode($key);
+  }
+
+
+  /**
+   * Loads an entity based on a bulk form key.
+   *
+   * @param string $bulk_form_key
+   *   The bulk form key representing the entity's id, language and revision (if
+   *   applicable) as one string.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The entity loaded in the state (language, optionally revision) specified
+   *   as part of the bulk form key.
+   */
+  protected function loadEntityFromBulkFormKey($bulk_form_key) {
+    $key = base64_decode($bulk_form_key);
+    $key_parts = json_decode($key);
+    $revision_id = NULL;
+
+    // If there are 3 items, vid will be last.
+    if (count($key_parts) === 3) {
+      $revision_id = array_pop($key_parts);
+    }
+
+    // The first two items will always be langcode and ID.
+    $id = array_pop($key_parts);
+    $langcode = array_pop($key_parts);
+
+    // Load the entity or a specific revision depending on the given key.
+    $storage = $this->entityTypeManager->getStorage($this->getEntityType());
+    $entity = $revision_id ? $storage->loadRevision($revision_id) : $storage->load($id);
+
+    if ($entity instanceof TranslatableInterface) {
+      $entity = $entity->getTranslation($langcode);
+    }
+
+    return $entity;
+  }
+
+}
diff --git a/src/Plugin/views/field/DiffTo.php b/src/Plugin/views/field/DiffTo.php
new file mode 100644
index 0000000..f9555e1
--- /dev/null
+++ b/src/Plugin/views/field/DiffTo.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\diff\Plugin\views\field\DiffFrom.
+ */
+
+namespace Drupal\diff\Plugin\views\field;
+
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * @ViewsField("diff__to")
+ */
+class DiffTo extends DiffPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+
+    $options['label']['default'] = t('To');
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewsForm(&$form, FormStateInterface $form_state) {
+    $use_revision = array_key_exists('revision', $this->view->getQuery()->getEntityTableInfo());
+
+    if (!empty($this->view->result)) {
+      $form[$this->options['id']]['#tree'] = TRUE;
+      foreach ($this->view->result as $row_index => $row) {
+        $entity = $row->_entity;
+        $form[$this->options['id']][$row_index] = [
+          '#type' => 'radio',
+          '#parents' => [$this->options['id']],
+          '#title' => $this->t('Compare this item'),
+          '#title_display' => 'invisible',
+          '#return_value' => $this->calculateEntityBulkFormKey($entity, $use_revision),
+        ];
+      }
+    }
+  }
+
+}
