diff --git a/diff.info.yml b/diff.info.yml
index af37c2d..b07f738 100644
--- a/diff.info.yml
+++ b/diff.info.yml
@@ -4,3 +4,5 @@ description: Shows differences between content revisions.
 core: 8.x
 version: VERSION
 configure: diff.general_settings
+dependencies:
+  - entity
diff --git a/modules/diff_test/diff_test.info.yml b/modules/diff_test/diff_test.info.yml
index 939f946..e7762a0 100644
--- a/modules/diff_test/diff_test.info.yml
+++ b/modules/diff_test/diff_test.info.yml
@@ -4,6 +4,7 @@ dependencies:
   - diff
   - filter
   - editor
+  - entity_test
 hidden: true
 name: Diff tests
 package: diff
diff --git a/modules/diff_test/diff_test.module b/modules/diff_test/diff_test.module
index e69de29..db57b2b 100644
--- a/modules/diff_test/diff_test.module
+++ b/modules/diff_test/diff_test.module
@@ -0,0 +1,15 @@
+<?php
+
+use Drupal\diff\Routing\DiffRouteProvider;
+
+/**
+ * Implements hook_entity_type_alter().
+ */
+function diff_test_entity_type_alter(array &$entity_types) {
+  /** @var \Drupal\Core\Entity\EntityType $entity_type */
+  $entity_type = $entity_types['entity_test_rev'];
+  $handlers = $entity_type->get('handlers');
+  $handlers['route_provider']['html_diff'] = DiffRouteProvider::class;
+  $entity_type->set('handlers', $handlers);
+  $entity_type->setLinkTemplate('revisions-diff', "/entity_test_rev/{entity_test_rev}/revision/{left_revision}/{right_revision}");
+}
diff --git a/src/Controller/GenericRevisionController.php b/src/Controller/GenericRevisionController.php
new file mode 100644
index 0000000..1493fb2
--- /dev/null
+++ b/src/Controller/GenericRevisionController.php
@@ -0,0 +1,331 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\diff\Controller\GenericRevisionController.
+ */
+
+namespace Drupal\diff\Controller;
+
+use Drupal\Component\Utility\Xss;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
+use Drupal\diff\EntityComparisonBase;
+use Drupal\entity\Revision\EntityRevisionLogInterface;
+use Drupal\node\NodeInterface;
+
+class GenericRevisionController extends EntityComparisonBase {
+
+  protected function getVids(EntityStorageInterface $storage, $entity_id) {
+    $result = $storage->getQuery()
+      ->allRevisions()
+      ->condition($storage->getEntityType()->getKey('id'), $entity_id)
+      ->execute();
+    return array_keys($result);
+  }
+
+  /**
+   * Returns a table which shows the differences between two entity revisions.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   * @param \Drupal\Core\Entity\EntityInterface $left_revision
+   *   The left revision
+   * @param \Drupal\Core\Entity\EntityInterface $right_revision
+   *   The right revision.
+   * @param string $filter
+   *   If $filter == 'raw' raw text is compared (including html tags)
+   *   If filter == 'raw-plain' markdown function is applied to the text before comparison.
+   *
+   * @return array
+   *   Table showing the diff between the two entity revisions.
+   */
+  public function compareEntityRevisions(RouteMatchInterface $route_match, EntityInterface $left_revision, EntityInterface $right_revision, $filter) {
+    $entity_type_id = $left_revision->getEntityTypeId();
+    $entity = $route_match->getParameter($entity_type_id);
+    $diff_rows = array();
+    $build = array(
+      '#title' => $this->t('Revisions for %title', array('%title' => $entity->label())),
+    );
+    if (!in_array($filter, array('raw', 'raw-plain'))) {
+      $filter = 'raw';
+    }
+    elseif ($filter == 'raw-plain') {
+      $filter = 'raw_plain';
+    }
+
+    $entity_type_id = $entity->getEntityTypeId();
+    $storage = $this->entityTypeManager()->getStorage($entity_type_id);
+    $vids = $this->getVids($storage, $entity->id());
+    $diff_rows[] = $this->buildRevisionsNavigation($entity, $vids, $left_revision->getRevisionId(), $right_revision->getRevisionId());
+    $diff_rows[] = $this->buildMarkdownNavigation($entity, $left_revision->getRevisionId(), $right_revision->getRevisionId(), $filter);
+    $diff_header = $this->buildTableHeader($left_revision, $right_revision);
+
+    // Perform comparison only if both entity revisions loaded successfully.
+    if ($left_revision != FALSE && $right_revision != FALSE) {
+      $fields = $this->compareRevisions($left_revision, $right_revision);
+      $entity_base_fields = $this->entityManager()->getBaseFieldDefinitions($entity_type_id);
+      // Check to see if we need to display certain fields or not based on
+      // selected view mode display settings.
+      foreach ($fields as $field_name => $field) {
+        // If we are dealing with entities only compare those fields
+        // set as visible from the selected view mode.
+        $view_mode = $this->config->get('content_type_settings.' . $entity->bundle() . '.view_mode');
+        // If no view mode is selected use the default view mode.
+        if ($view_mode == NULL) {
+          $view_mode = 'default';
+        }
+        $visible = entity_get_display($entity_type_id, $entity->bundle(), $view_mode)->getComponent($field_name);
+        if ($visible == NULL && !array_key_exists($field_name, $entity_base_fields)) {
+          unset($fields[$field_name]);
+        }
+      }
+      // Build the diff rows for each field and append the field rows
+      // to the table rows.
+      foreach ($fields as $field) {
+        $field_label_row = '';
+        if (!empty($field['#name'])) {
+          $field_label_row = array(
+            'data' => $this->t('Changes to %name', array('%name' => $field['#name'])),
+            'colspan' => 4,
+            'class' => array('field-name'),
+          );
+        }
+        $field_diff_rows = $this->getRows(
+          $field['#states'][$filter]['#left'],
+          $field['#states'][$filter]['#right']
+        );
+
+        // Add the field label to the table only if there are changes to that field.
+        if (!empty($field_diff_rows) && !empty($field_label_row)) {
+          $diff_rows[] = array($field_label_row);
+        }
+
+        // Add field diff rows to the table rows.
+        $diff_rows = array_merge($diff_rows, $field_diff_rows);
+      }
+
+      // Add the CSS for the diff.
+      $build['#attached']['library'][] = 'diff/diff.general';
+      $theme = $this->config->get('general_settings.theme');
+      if ($theme) {
+        if ($theme == 'default') {
+          $build['#attached']['library'][] = 'diff/diff.default';
+        }
+        elseif ($theme == 'github') {
+          $build['#attached']['library'][] = 'diff/diff.github';
+        }
+      }
+      // If the setting could not be loaded or is missing use the default theme.
+      elseif ($theme == NULL) {
+        $build['#attached']['library'][] = 'diff/diff.github';
+      }
+
+      $build['diff'] = array(
+        '#type' => 'table',
+        '#header' => $diff_header,
+        '#rows' => $diff_rows,
+        '#empty' => $this->t('No visible changes'),
+        '#attributes' => array(
+          'class' => array('diff'),
+        ),
+      );
+
+      if ($entity->hasLinkTemplate('version-history')) {
+        $build['back'] = array(
+          '#type' => 'link',
+          '#attributes' => array(
+            'class' => array(
+              'button',
+              'diff-button',
+            ),
+          ),
+          '#title' => $this->t('Back to Revision Overview'),
+          '#url' => Url::fromRoute("entity.$entity_type_id.version_history", [$entity_type_id => $entity->id()]),
+        );
+      }
+
+      return $build;
+    }
+    else {
+      // @todo When task 'Convert drupal_set_message() to a service' (2278383)
+      //   will be merged use the corresponding service instead.
+      drupal_set_message($this->t('Selected @label revisions could not be loaded.', ['@label' => $entity->getEntityType()->getLabel()]), 'error');
+    }
+  }
+
+  /**
+   * Build the header for the diff table.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $left_revision
+   *   Revision from the left hand side.
+   * @param \Drupal\Core\Entity\EntityInterface $right_revision
+   *   Revision from the right hand side.
+   *
+   * @return array
+   *   Header for Diff table.
+   */
+  protected function buildTableHeader(EntityInterface $left_revision, EntityInterface $right_revision) {
+    $entity_type_id = $left_revision->getEntityTypeId();
+    $revisions = array($left_revision, $right_revision);
+    $header = array();
+
+    foreach ($revisions as $revision) {
+      if ($revision instanceof EntityRevisionLogInterface || $revision instanceof NodeInterface) {
+        $revision_log = $this->nonBreakingSpace;
+
+        if ($revision instanceof EntityRevisionLogInterface) {
+          $revision_log = Xss::filter($revision->getRevisionLogMessage());
+        }
+        elseif ($revision instanceof NodeInterface) {
+          $revision_log = $revision->revision_log->value;
+        }
+        $username = array(
+          '#theme' => 'username',
+          '#account' => $revision->uid->entity,
+        );
+        $revision_date = $this->date->format($revision->getRevisionCreationTime(), 'short');
+        $revision_link = $this->t($revision_log . '@date', array(
+            '@date' => $this->l($revision_date, Url::fromRoute("entity.$entity_type_id.revision", array(
+              $entity_type_id => $revision->id(),
+              $entity_type_id . '_revision' => $revision->getRevisionId(),
+          ))),
+        ));
+      }
+      else {
+        $revision_link = $this->l($revision->label(), $revision->toUrl('revision'));
+      }
+
+      // @todo When theming think about where in the table to integrate this
+      //   link to the revision user. There is some issue about multi-line headers
+      //   for theme table.
+      // $header[] = array(
+      //   'data' => $this->t('by' . '!username', array('!username' => drupal_render($username))),
+      //   'colspan' => 1,
+      // );
+      $header[] = array(
+        'data' => array('#markup' => $this->nonBreakingSpace),
+        'colspan' => 1,
+      );
+      $header[] = array(
+        'data' => array('#markup' => $revision_link),
+        'colspan' => 1,
+      );
+    }
+
+    return $header;
+  }
+
+  /**
+   * Returns the navigation row for diff table.
+   */
+  protected function buildRevisionsNavigation(EntityInterface $entity, $vids, $left_vid, $right_vid) {
+    $entity_type_id = $entity->getEntityTypeId();
+    $entity_id = $entity->id();
+    $revisions_count = count($vids);
+    $i = 0;
+
+    $row = array();
+    // Find the previous revision.
+    while ($left_vid > $vids[$i]) {
+      $i += 1;
+    }
+    if ($i != 0) {
+      // Second column.
+      $row[] = array(
+        'data' => $this->l(
+          $this->t('< Previous difference'),
+          Url::fromRoute("entity.$entity_type_id.revisions_diff",
+            array(
+              $entity_type_id => $entity_id,
+              'left_revision' => $vids[$i - 1],
+              'right_revision' => $left_vid,
+            ))
+        ),
+        'colspan' => 2,
+        'class' => 'rev-navigation',
+      );
+    }
+    else {
+      // Second column.
+      $row[] = $this->nonBreakingSpace;
+    }
+    // Third column.
+    $row[] = $this->nonBreakingSpace;
+    // Find the next revision.
+    $i = 0;
+    while ($i < $revisions_count && $right_vid >= $vids[$i]) {
+      $i += 1;
+    }
+    if ($revisions_count != $i && $vids[$i - 1] != $vids[$revisions_count - 1]) {
+      // Forth column.
+      $row[] = array(
+        'data' => $this->l(
+          $this->t('Next difference >'),
+          Url::fromRoute("entity.$entity_type_id.revisions_diff",
+            array(
+              $entity_type_id => $entity_id,
+              'left_revision' => $right_vid,
+              'right_revision' => $vids[$i],
+            ))
+        ),
+        'colspan' => 2,
+        'class' => 'rev-navigation',
+      );
+    }
+    else {
+      // Forth column.
+      $row[] = $this->nonBreakingSpace;
+    }
+
+    // If there are only 2 revision return an empty row.
+    if ($revisions_count == 2) {
+      return array();
+    }
+    else {
+      return $row;
+    }
+  }
+
+  /**
+   * Builds a table row with navigation between raw and raw-plain formats.
+   */
+  protected function buildMarkdownNavigation(EntityInterface $entity, $left_vid, $right_vid, $active_filter) {
+    $entity_type_id = $entity->getEntityTypeId();
+
+    $links['raw'] = array(
+      'title' => $this->t('Standard'),
+      'url' => Url::fromRoute("entity.$entity_type_id.revisions_diff", array(
+        $entity_type_id => $entity->id(),
+        'left_revision' => $left_vid,
+        'right_revision' => $right_vid,
+      )),
+    );
+    $links['raw_plain'] = array(
+      'title' => $this->t('Markdown'),
+      'url' => Url::fromRoute("entity.$entity_type_id.revisions_diff", array(
+        $entity_type_id => $entity->id(),
+        'left_revision' => $left_vid,
+        'right_revision' => $right_vid,
+        'filter' => 'raw-plain',
+      )),
+    );
+
+    // Set as the first element the current filter.
+    $filter = $links[$active_filter];
+    unset($links[$active_filter]);
+    array_unshift($links, $filter);
+
+    $row[] = array(
+      'data' => array(
+        '#type' => 'operations',
+        '#links' => $links,
+      ),
+      'colspan' => 4,
+    );
+
+    return $row;
+  }
+}
diff --git a/src/Routing/DiffRouteProvider.php b/src/Routing/DiffRouteProvider.php
new file mode 100644
index 0000000..9d27c07
--- /dev/null
+++ b/src/Routing/DiffRouteProvider.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\diff\Routing\DiffRouteProvider.
+ */
+
+namespace Drupal\diff\Routing;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Contains routes for diff functionality.
+ */
+class DiffRouteProvider implements EntityRouteProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = new RouteCollection();
+    if ($route = $this->getDiffRoute($entity_type)) {
+      $collection->add('entity.' . $entity_type->id() . '.revisions_diff', $route);
+    }
+    return $collection;
+  }
+
+  /**
+   * Constructs the diff route.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The diff route.
+   */
+  protected function getDiffRoute(EntityTypeInterface $entity_type) {
+    if ($entity_type->hasLinkTemplate('revisions-diff')) {
+      $route = new Route($entity_type->getLinkTemplate('revisions-diff'));
+      $route->addDefaults([
+        '_controller' => '\Drupal\diff\Controller\GenericRevisionController::compareEntityRevisions',
+        'filter' => 'raw',
+      ]);
+      $route->addRequirements([
+        '_entity_access' => $entity_type->id() . '.view',
+      ]);
+      $route->setOption('parameters', [
+        $entity_type->id() => ['type' => 'entity:' . $entity_type->id()],
+        'left_revision' => ['type' => 'entity_revision:' . $entity_type->id()],
+        'right_revision' => ['type' => 'entity_revision:' . $entity_type->id()],
+      ]);
+      return $route;
+    }
+  }
+
+}
diff --git a/tests/src/Kernel/DiffControllerTest.php b/tests/src/Kernel/DiffControllerTest.php
new file mode 100644
index 0000000..100e0bd
--- /dev/null
+++ b/tests/src/Kernel/DiffControllerTest.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\diff\Kernel\DiffControllerTest.
+ */
+
+namespace Drupal\Tests\diff\Kernel;
+
+use Drupal\Core\Url;
+use Drupal\entity_test\Entity\EntityTestRev;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\user\Entity\Role;
+use Drupal\user\Entity\User;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests the diff controller.
+ *
+ * @group diff
+ */
+class DiffControllerTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['diff', 'entity_test', 'diff_test', 'entity', 'system', 'user'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('user');
+    $this->installSchema('system', ['router', 'sequences']);
+    $this->installSchema('user', 'users_data');
+    \Drupal::service('router.builder')->rebuild();
+
+    $this->installConfig('diff');
+    $config = \Drupal::configFactory()->getEditable('diff.settings');
+    $config->set('entity.entity_test_rev.name', TRUE);
+    $config->save();
+  }
+
+  public function testController() {
+    $entity = EntityTestRev::create([
+      'name' => 'test entity 1',
+      'type' => 'entity_test_rev',
+    ]);
+    $entity->save();
+    $vid1 = $entity->getRevisionId();
+
+    $entity->name->value = 'test entity 2';
+    $entity->setNewRevision(TRUE);
+    $entity->save();
+    $vi2 = $entity->getRevisionId();
+
+    /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
+    $http_kernel = \Drupal::service('http_kernel');
+    $request = Request::create(Url::fromRoute('entity.entity_test_rev.revisions_diff', ['node' => $entity->id(), 'entity_test_rev' => $entity->id(), 'left_revision' => $vid1, 'right_revision' => $vi2])->toString(TRUE)->getGeneratedUrl());
+
+    $response = $http_kernel->handle($request);
+    $this->assertEquals(403, $response->getStatusCode());
+
+
+    $role = Role::create([
+      'id' => 'test_role',
+    ]);
+    $role->grantPermission('administer entity_test content');
+    $role->save();
+    $account = User::create([
+      'name' => 'test user',
+      'roles' => $role->id(),
+    ]);
+    $account->save();
+
+    \Drupal::currentUser()->setAccount($account);
+    $response = $http_kernel->handle($request);
+    $this->assertEquals(200, $response->getStatusCode());
+
+    $output = $response->getContent();
+    $this->assertContains('<td class="diff-context diff-deletedline">test entity <span class="diffchange">1</span></td>', $output);
+    $this->assertContains('<td class="diff-context diff-addedline">test entity <span class="diffchange">2</span></td>', $output);
+  }
+
+
+}
