diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f71749cb284d9412caeb0910e82113ae5f3b35b2..66b7952b8507e163e94d39155a7fc1a71a538307 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -22,23 +22,9 @@ variables:
   _PHPUNIT_CONCURRENT: "1"
   _PHPUNIT_EXTRA: "--suppress-deprecations"
   OPT_IN_TEST_NEXT_MAJOR: '0'
-  # Not Drupal 11 compatable yet.
   OPT_IN_TEST_CURRENT: '1'
   OPT_IN_TEST_PREVIOUS_MAJOR: '1'
 
-# Rules for PHPStan on Drupal 10.
-.opt-in-previous-major-rule: &opt-in-previous-major-rule
-  if: '$OPT_IN_TEST_PREVIOUS_MAJOR != "1" || $CORE_PREVIOUS_STABLE == ""'
-  when: never
-.skip-phpstan-rule: &skip-phpstan-rule
-  if: '$SKIP_PHPSTAN == "1"'
-  when: never
-.php-files-exist-rule: &php-files-exist-rule
-  - exists:
-      - "*.{$DRUPAL_PHP_FILE_TYPES}"
-      - "**/*.{$DRUPAL_PHP_FILE_TYPES}"
-    when: on_success
-
 #
 # Linting jobs are passing so any issue that breaks them should fix them.
 #
@@ -52,15 +38,7 @@ phpcs:
     - vendor/bin/phpcs -s $_WEB_ROOT/modules/custom --report-junit=junit.xml --report-full --report-summary --report-source
   allow_failure: false
 phpstan:
-  # Allow PHPStan to run on Drupal 10.
-  rules:
-    - *opt-in-previous-major-rule
-    - *skip-phpstan-rule
-    - *php-files-exist-rule
-  needs:
-    - "composer (previous major)"
   allow_failure: false
-
 phpstan (next major):
   allow_failure: true
 stylelint:
diff --git a/entity_usage_updater.info.yml b/entity_usage_updater.info.yml
index 2c85bd2cdec24573113b39fad17e559a646cad66..c874335776648ace9f382a38c9d3303c210bd81b 100644
--- a/entity_usage_updater.info.yml
+++ b/entity_usage_updater.info.yml
@@ -1,7 +1,7 @@
 name: Entity Usage Updater
 type: module
 description: Lets you update entity references tracked by the Entity Usage module.
-core_version_requirement: ^10 || ^11
+core_version_requirement: ^10.2 || ^11
 dependencies:
   - entity_usage:entity_usage (8.x-2.x)
 test_dependencies:
diff --git a/entity_usage_updater.services.yml b/entity_usage_updater.services.yml
index 50c0419aba55de2aed8991f6075f9176b8db3235..9777a92f15f1b77221e55789c165fcd95bfd320c 100644
--- a/entity_usage_updater.services.yml
+++ b/entity_usage_updater.services.yml
@@ -1,4 +1,7 @@
 services:
+  _defaults:
+    autoconfigure: true
+    autowire: true
 
   entity_usage_updater.updater:
     class: Drupal\entity_usage_updater\EntityUsageUpdater
@@ -10,3 +13,5 @@ services:
   plugin.manager.entity_usage_updater:
     class: Drupal\entity_usage_updater\EntityUsageUpdaterPluginManager
     parent: default_plugin_manager
+
+  Drupal\entity_usage_updater\Event\EntityUsageUpdaterControllerSubscriber: ~
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 3d75900371367abae2721ee7e65cf050633479df..a2fdbc246acc487450bb4d50ece255c9fe212e22 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,21 +1,12 @@
 parameters:
 	ignoreErrors:
-		-
-			message: "#^Call to an undefined method Drupal\\\\Core\\\\Entity\\\\EntityInterface\\:\\:getTranslation\\(\\)\\.$#"
-			count: 1
-			path: src/EntityUsageUpdater.php
-
 		-
 			message: "#^Call to an undefined method Drupal\\\\Core\\\\Entity\\\\EntityStorageInterface\\:\\:getLatestRevisionId\\(\\)\\.$#"
 			count: 1
 			path: src/EntityUsageUpdater.php
 
 		-
-			message: """
-				#^Call to deprecated method loadRevision\\(\\) of interface Drupal\\\\Core\\\\Entity\\\\EntityStorageInterface\\:
-				in drupal\\:10\\.1\\.0 and is removed from drupal\\:11\\.0\\.0\\. Use
-				\\\\Drupal\\\\Core\\\\Entity\\\\RevisionableStorageInterface\\:\\:loadRevision instead\\.$#
-			"""
+			message: "#^Call to an undefined method Drupal\\\\Core\\\\Entity\\\\EntityStorageInterface\\:\\:loadRevision\\(\\)\\.$#"
 			count: 1
 			path: src/EntityUsageUpdater.php
 
diff --git a/src/Controller/LocalTaskUsageController.php b/src/Controller/LocalTaskUsageController.php
new file mode 100644
index 0000000000000000000000000000000000000000..d0115344da02094620378dc3554580542d1784b9
--- /dev/null
+++ b/src/Controller/LocalTaskUsageController.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\entity_usage_updater\Controller;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\entity_usage\Controller\LocalTaskUsageController as BaseLocalTaskUsageController;
+use Drupal\entity_usage_updater\Form\EntityUsageUpdateForm;
+
+/**
+ * Controller to add form to entity usage local tasks.
+ */
+class LocalTaskUsageController extends BaseLocalTaskUsageController {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listUsagePage($entity_type, $entity_id): array {
+    $build = parent::listUsagePage($entity_type, $entity_id);
+    // Only add the form if there are usages.
+    if (array_keys($build) !== ['#markup']) {
+      $entity = $this->entityTypeManager->getStorage($entity_type)->load($entity_id);
+      $form = $this->formBuilder()->getForm(EntityUsageUpdateForm::class, $entity);
+      // @todo add ability to move the form?
+      $build['entity_usage_updater'] = [
+        '#type' => 'details',
+        '#title' => t('Update usages'),
+        '#tree' => FALSE,
+        '#open' => FALSE,
+      ];
+      $build['entity_usage_updater']['form'] = $form;
+    }
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listUsageLocalTask(RouteMatchInterface $route_match) {
+    $entity = $this->getEntityFromRouteMatch($route_match);
+    return $this->listUsagePage($entity->getEntityTypeId(), $entity->id());
+  }
+
+}
diff --git a/src/Event/EntityUsageUpdaterControllerSubscriber.php b/src/Event/EntityUsageUpdaterControllerSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..af82d899d6d71314858e89649ba61215ac778200
--- /dev/null
+++ b/src/Event/EntityUsageUpdaterControllerSubscriber.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\entity_usage_updater\Event;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Swaps the entity usage tab controller for the one provided by this module.
+ */
+class EntityUsageUpdaterControllerSubscriber extends RouteSubscriberBase {
+
+  public function __construct(protected EntityTypeManagerInterface $entityTypeManager) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection): void {
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      $route = $collection->get("entity.$entity_type_id.entity_usage");
+      if ($route) {
+        $route->setDefault('_controller', '\Drupal\entity_usage_updater\Controller\LocalTaskUsageController::listUsageLocalTask');
+      }
+    }
+
+    $route = $collection->get('entity_usage.usage_list');
+    if ($route) {
+      $route->setDefault('_controller', '\Drupal\entity_usage_updater\Controller\LocalTaskUsageController::listUsagePage');
+    }
+  }
+
+}
diff --git a/src/Form/EntityUsageUpdateForm.php b/src/Form/EntityUsageUpdateForm.php
index 4d1d8d88da4af672bed4be9c351f64b44f3e85a2..9a31b689c88826a90558fb35ac684b5c89a269c6 100644
--- a/src/Form/EntityUsageUpdateForm.php
+++ b/src/Form/EntityUsageUpdateForm.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace Drupal\entity_usage_updater\Form;
 
 use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\FormBase;
@@ -51,7 +52,7 @@ class EntityUsageUpdateForm extends FormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state) {
+  public function buildForm(array $form, FormStateInterface $form_state, ?EntityInterface $entity = NULL) {
     $is_content_entity_type = fn(EntityTypeInterface $type): bool => $type instanceof ContentEntityTypeInterface;
     $types = array_filter($this->entityTypeManager->getDefinitions(), $is_content_entity_type);
     $options = [];
@@ -60,21 +61,33 @@ class EntityUsageUpdateForm extends FormBase {
     }
     asort($options, SORT_LOCALE_STRING);
 
-    $form['entity_type_id'] = [
-      '#title' => $this->t("Entity type"),
-      '#type' => 'select',
-      '#options' => $options,
-      '#required' => TRUE,
-      '#default_value' => isset($options['node']) ? 'node' : NULL,
-    ];
-
-    $form['old_entity_id'] = [
-      '#title' => $this->t("Target entity ID"),
-      '#description' => $this->t("Enter the ID of the entity you no longer want referenced."),
-      '#type' => 'textfield',
-      '#required' => TRUE,
-      '#size' => 20,
-    ];
+    if ($entity) {
+      $form['entity_type_id'] = [
+        '#type' => 'value',
+        '#value' => $entity->getEntityTypeId(),
+      ];
+      $form['old_entity_id'] = [
+        '#type' => 'value',
+        '#value' => $entity->id(),
+      ];
+    }
+    else {
+      $form['entity_type_id'] = [
+        '#title' => $this->t("Entity type"),
+        '#type' => 'select',
+        '#options' => $options,
+        '#required' => TRUE,
+        '#default_value' => isset($options['node']) ? 'node' : NULL,
+      ];
+
+      $form['old_entity_id'] = [
+        '#title' => $this->t("Target entity ID"),
+        '#description' => $this->t("Enter the ID of the entity you no longer want referenced."),
+        '#type' => 'textfield',
+        '#required' => TRUE,
+        '#size' => 20,
+      ];
+    }
 
     $form['new_entity_id'] = [
       '#title' => $this->t("Replacement entity ID"),
diff --git a/src/Plugin/EntityUsageUpdater/HtmlLink.php b/src/Plugin/EntityUsageUpdater/HtmlLink.php
index 304caa51aa2a4825a375921e8bae528d333f97ff..ce4898869d206e97654fa148777d7694aa15571c 100644
--- a/src/Plugin/EntityUsageUpdater/HtmlLink.php
+++ b/src/Plugin/EntityUsageUpdater/HtmlLink.php
@@ -123,7 +123,7 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
         $this->updateItemProperty($old_target, $new_entity_type, $new_id, $summary);
       }
     }
-    catch (EntityUsageUpdaterException $e) {
+    catch (EntityUsageUpdaterException) {
       $host = $item->getEntity();
       $context = [
         '@host_entity_type' => $host->getEntityType()->getLabel(),
@@ -177,9 +177,10 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
         $options = [
           'fragment' => $parts['fragment'],
           'query' => $parts['query'],
+          'absolute' => CoreUrlHelper::isExternal($href),
         ];
 
-        if ($this->configuration['convert_to_linkit']) {
+        if ($this->configuration['convert_to_linkit'] ?? FALSE) {
           $pattern = '~<a\s+[^>]*href\s*=\s*([\'"])' . preg_quote($href, '~') . '\1(\s+[^>]*)?>~i';
           $new_href = Url::fromRoute("entity.$new_entity_type.canonical", [$new_entity_type => $new_id], $options)->toString();
           $uuid = $this->getUuid($new_entity_type, $new_id);
@@ -236,7 +237,7 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
           yield [$entity, $element];
         }
       }
-      catch (\DOMException $e) {
+      catch (\DOMException) {
         // Do nothing.
       }
     }
diff --git a/tests/src/Functional/ListControllerTest.php b/tests/src/Functional/ListControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..20aa4a9601a7862f6349f7c4435151013d2c2c6a
--- /dev/null
+++ b/tests/src/Functional/ListControllerTest.php
@@ -0,0 +1,150 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\entity_usage_updater\Functional;
+
+use Drupal\filter\Entity\FilterFormat;
+use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\entity_usage\Traits\EntityUsageLastEntityQueryTrait;
+
+/**
+ * Tests the tab listing the usage of a given entity with this module installed.
+ *
+ * @group entity_usage_updater
+ */
+class ListControllerTest extends BrowserTestBase {
+
+  protected $defaultTheme = 'stark';
+
+  use EntityUsageLastEntityQueryTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'node',
+    'field_ui',
+    'text',
+    'path',
+    'entity_usage_updater',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(): void {
+    parent::setUp();
+
+    $account = $this->drupalCreateUser([
+      'access entity usage statistics',
+      'administer nodes',
+      'bypass node access',
+    ]);
+    $this->drupalLogin($account);
+
+    $this->drupalCreateContentType(['type' => 'page']);
+
+    // Set up the filter formats used by this test.
+    $basic_html_format = FilterFormat::create([
+      'format' => 'basic_html',
+      'name' => 'Basic HTML',
+      'filters' => [
+        'filter_html' => [
+          'status' => 1,
+          'settings' => [
+            'allowed_html' => '<p> <br> <strong> <a href> <em>',
+          ],
+        ],
+      ],
+    ]);
+    $basic_html_format->save();
+    user_role_grant_permissions('authenticated', [$basic_html_format->getPermissionName()]);
+
+    $current_request = \Drupal::request();
+    $config = \Drupal::configFactory()->getEditable('entity_usage.settings');
+    $config->set('site_domains', [$current_request->getHttpHost() . $current_request->getBasePath()]);
+    $config->save();
+    $this->config('entity_usage.settings')->set('local_task_enabled_entity_types', ['node'])->save();
+    \Drupal::service('router.builder')->rebuild();
+  }
+
+  /**
+   * Tests the page listing the usage of entities.
+   *
+   * @covers \Drupal\entity_usage\Controller\ListUsageController::listUsagePage
+   */
+  public function testListController(): void {
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    // Create node 1.
+    $this->drupalGet('node/add/page');
+    $page->fillField('title[0][value]', 'Node 1');
+    $page->pressButton('Save');
+    $assert_session->pageTextContains('Node 1 has been created.');
+    /** @var \Drupal\node\NodeInterface $node1 */
+    $node1 = $this->drupalGetNodeByTitle('Node 1');
+
+    // Create node 2.
+    $this->drupalGet('node/add/page');
+    $page->fillField('title[0][value]', 'Node 2');
+    $page->pressButton('Save');
+    $assert_session->pageTextContains('Node 2 has been created.');
+    $node2 = $this->drupalGetNodeByTitle('Node 2');
+
+    // Create node 3.
+    $this->drupalGet('node/add/page');
+    $page->fillField('title[0][value]', 'Node 3');
+    $page->fillField('body[0][value]', (string) $node1->toLink("Link to content", options: ['absolute' => TRUE])->toString());
+    // $page->fillField('body[0][value]', 'Testing!!!!!');
+    $page->pressButton('Save');
+    $assert_session->pageTextContains('Node 3 has been created.');
+    $node3 = $this->drupalGetNodeByTitle('Node 3');
+
+    $this->assertSession()->linkByHrefExists($node1->toUrl()->toString());
+    $this->assertSession()->linkByHrefNotExists($node2->toUrl()->toString());
+
+    $this->drupalGet('node/1/usage');
+    $this->assertSession()->fieldExists('new_entity_id')->setValue($node2->id());
+    $this->assertSession()->buttonExists('Update')->press();
+    // Process the batch.
+    $this->checkForMetaRefresh();
+
+    // Check to see if the link has been replaced.
+    $this->drupalGet('node/3');
+    $this->assertSession()->linkByHrefExists($node2->toUrl()->toString());
+    $this->assertSession()->linkByHrefNotExists($node1->toUrl()->toString());
+
+    $this->drupalGet('node/3/usage');
+    // Ensure the form for updating is not present if there are no usages.
+    $this->assertSession()->pageTextContains('There are no recorded usages for entity of type: node with id: 3');
+    $this->assertSession()->fieldNotExists('new_entity_id');
+
+    // Ensure that if usages only exist in old revisions then they are not
+    // replaced even though the form is present.
+    // @todo can we remove the form in if they are no current usages?
+    $this->drupalGet('node/1/usage');
+    $this->assertSession()->fieldExists('new_entity_id')->setValue($node2->id());
+    $this->assertSession()->buttonExists('Update')->press();
+    // Process the batch.
+    $this->checkForMetaRefresh();
+    // Check to see if the link has not been replaced.
+    $this->drupalGet('node/3');
+    $this->assertSession()->linkByHrefExists($node2->toUrl()->toString());
+    $this->assertSession()->linkByHrefNotExists($node1->toUrl()->toString());
+
+    // Ensure the non-tab entity usage page works too.
+    $this->drupalGet("admin/content/entity-usage/node/2");
+    $this->assertSession()->fieldExists('new_entity_id')->setValue($node1->id());
+    $this->assertSession()->buttonExists('Update')->press();
+    // Process the batch.
+    $this->checkForMetaRefresh();
+    // Check to see if the link has not been replaced.
+    $this->drupalGet('node/3');
+    $this->assertSession()->linkByHrefExists($node1->toUrl()->toString());
+    $this->assertSession()->linkByHrefNotExists($node2->toUrl()->toString());
+  }
+
+}