diff --git a/entity_usage.install b/entity_usage.install
index f573370053640cad27745297bf7f4a9c8d75d7d9..4fafe05637c760542652bd41c89a7c64399a0670 100644
--- a/entity_usage.install
+++ b/entity_usage.install
@@ -77,7 +77,7 @@ function entity_usage_schema(): array {
       'field_name' => [
         'description' => 'The field in the source entity containing the target entity.',
         'type' => 'varchar_ascii',
-        'length' => 128,
+        'length' => 1024,
         'not null' => TRUE,
         'default' => '',
       ],
@@ -116,3 +116,21 @@ function entity_usage_schema(): array {
 function entity_usage_update_last_removed(): int {
   return 8206;
 }
+
+/**
+ * Increase field_name length in entity_usage table.
+ */
+function entity_usage_update_8208(): void {
+  \Drupal::database()->schema()->changeField(
+    'entity_usage',
+    'field_name',
+    'field_name',
+    [
+      'type' => 'varchar_ascii',
+      'length' => 1024,
+      'not null' => TRUE,
+      'default' => '',
+      'description' => 'The field in the source entity containing the target entity.',
+    ]
+  );
+}
diff --git a/entity_usage.module b/entity_usage.module
index 2597e34aea8cf7414370f15637e74d13f7989f27..c4bf296179b3f502b80cbf3bae5543dbe7a0f84e 100644
--- a/entity_usage.module
+++ b/entity_usage.module
@@ -57,6 +57,7 @@ function entity_usage_entity_insert(EntityInterface $entity): void {
  * Implements hook_entity_update().
  */
 function entity_usage_entity_update(EntityInterface $entity): void {
+  \Drupal::service('entity_usage.paragraph_inherited_usage_updater')->queueEntity($entity);
   $entity_usage_update_manager = \Drupal::service('entity_usage.entity_update_manager');
   assert($entity_usage_update_manager instanceof EntityUpdateManagerInterface);
   $entity_usage_update_manager->trackUpdateOnEdition($entity);
@@ -70,6 +71,7 @@ function entity_usage_entity_update(EntityInterface $entity): void {
  * Implements hook_entity_predelete().
  */
 function entity_usage_entity_predelete(EntityInterface $entity): void {
+  \Drupal::service('entity_usage.paragraph_inherited_usage_updater')->queueEntity($entity);
   $entity_usage_update_manager = \Drupal::service('entity_usage.entity_update_manager');
   assert($entity_usage_update_manager instanceof EntityUpdateManagerInterface);
   $entity_usage_update_manager->trackUpdateOnDeletion($entity);
@@ -79,6 +81,7 @@ function entity_usage_entity_predelete(EntityInterface $entity): void {
  * Implements hook_entity_translation_delete().
  */
 function entity_usage_entity_translation_delete(EntityInterface $translation): void {
+  \Drupal::service('entity_usage.paragraph_inherited_usage_updater')->queueEntity($translation);
   $entity_usage_update_manager = \Drupal::service('entity_usage.entity_update_manager');
   assert($entity_usage_update_manager instanceof EntityUpdateManagerInterface);
   $entity_usage_update_manager->trackUpdateOnDeletion($translation, 'translation');
@@ -88,6 +91,7 @@ function entity_usage_entity_translation_delete(EntityInterface $translation): v
  * Implements hook_entity_revision_delete().
  */
 function entity_usage_entity_revision_delete(EntityInterface $entity): void {
+  \Drupal::service('entity_usage.paragraph_inherited_usage_updater')->queueEntity($entity);
   $entity_usage_update_manager = \Drupal::service('entity_usage.entity_update_manager');
   assert($entity_usage_update_manager instanceof EntityUpdateManagerInterface);
   $entity_usage_update_manager->trackUpdateOnDeletion($entity, 'revision');
diff --git a/entity_usage.services.yml b/entity_usage.services.yml
index 8103e645492650646c9b10853f37396608bd277f..0bf49c109d2cb572957f426e994a48d1da48e006 100644
--- a/entity_usage.services.yml
+++ b/entity_usage.services.yml
@@ -50,6 +50,7 @@ services:
 
   Drupal\entity_usage\PreSaveUrlRecorder: ~
   Drupal\entity_usage\RecreateTrackingDataForFieldQueuer: ~
+  entity_usage.recreate_tracking_data_for_field_queuer: '@Drupal\entity_usage\RecreateTrackingDataForFieldQueuer'
 
   # By default we enable Entity and Language subscribers to the
   # \Drupal\entity_usage\Events\Events::URL_TO_ENTITY event. If the file and/or
@@ -57,3 +58,12 @@ services:
   # \Drupal\entity_usage\EntityUsageServiceProvider::register().
   Drupal\entity_usage\UrlToEntityIntegrations\EntityRouting: ~
   Drupal\entity_usage\UrlToEntityIntegrations\LanguageIntegration: ~
+
+  entity_usage.paragraph_inherited_usage_updater:
+    class: Drupal\entity_usage\ParagraphInheritedUsageUpdater
+    arguments:
+      - '@entity_type.manager'
+      - '@plugin.manager.entity_usage.track'
+      - '@entity_usage.recreate_tracking_data_for_field_queuer'
+    tags:
+      - { name: needs_destruction }
diff --git a/src/EntityUpdateManager.php b/src/EntityUpdateManager.php
index 71e1768a2ca6c4c56ce19900a3dccf398e26a676..f8717ca4e1a96165a9b02573ffe91fac30ee8127 100644
--- a/src/EntityUpdateManager.php
+++ b/src/EntityUpdateManager.php
@@ -215,12 +215,9 @@ class EntityUpdateManager implements EntityUpdateManagerInterface {
   }
 
   /**
-   * Gets the enabled tracking plugins, all plugins are enabled by default.
-   *
-   * @return array<string, \Drupal\entity_usage\EntityUsageTrackInterface>
-   *   The enabled plugin instances keyed by plugin ID.
+   * {@inheritdoc}
    */
-  protected function getEnabledPlugins(): array {
+  public function getEnabledPlugins(): array {
     $all_plugin_ids = array_keys($this->trackManager->getDefinitions());
     $enabled_plugins = $this->config->get('track_enabled_plugins');
     $enabled_plugin_ids = is_array($enabled_plugins) ? $enabled_plugins : $all_plugin_ids;
diff --git a/src/EntityUpdateManagerInterface.php b/src/EntityUpdateManagerInterface.php
index 79e2d1dd626ec9ec140db0559e0d837a50db6e82..414ea57cb1e18c1bf2826b88c43cced46806124f 100644
--- a/src/EntityUpdateManagerInterface.php
+++ b/src/EntityUpdateManagerInterface.php
@@ -43,4 +43,12 @@ interface EntityUpdateManagerInterface {
    */
   public function trackUpdateOnDeletion(EntityInterface $entity, $type = 'default'): void;
 
+  /**
+   * Gets the enabled tracking plugins, all plugins are enabled by default.
+   *
+   * @return array<string, \Drupal\entity_usage\EntityUsageTrackInterface>
+   *   The enabled plugin instances keyed by plugin ID.
+   */
+  public function getEnabledPlugins(): array;
+
 }
diff --git a/src/EntityUsage.php b/src/EntityUsage.php
index 1a8a371e3ccede7ef0f6b0eb1c1866665348c788..43f825fa6042d5a6a61333a91bd9cd01b0fd15c4 100644
--- a/src/EntityUsage.php
+++ b/src/EntityUsage.php
@@ -469,28 +469,55 @@ class EntityUsage implements EntityUsageBulkInterface {
   /**
    * {@inheritdoc}
    */
-  public function listTargetEntitiesByFieldAndMethod(string|int $source_id, string $source_entity_type_id, string $source_langcode, string|int $source_vid, string $method, string $field_name): array {
+  public function listTargetEntitiesByFieldAndMethod(string|int $source_id, string $source_entity_type_id, string $source_langcode, string|int $source_vid, string $method, string $field_name, bool $return_as_arrays = FALSE): array {
     // Entities can have string IDs. We support that by using different columns
     // on each case.
     $source_id_column = $this->isInt($source_id) ? 'source_id' : 'source_id_string';
     $query = $this->connection->select($this->tableName, 'e')
-      ->fields('e', [
+      ->fields('e', array_filter([
         'target_id',
         'target_id_string',
         'target_type',
-      ])
+        $return_as_arrays ? 'method' : NULL,
+        $return_as_arrays ? 'field_name' : NULL,
+      ]))
       ->condition($source_id_column, $source_id)
       ->condition('source_type', $source_entity_type_id)
       ->condition('source_vid', $source_vid ?: 0)
-      ->condition('field_name', $field_name)
-      ->condition('method', $method)
       ->condition('count', 0, '>')
       ->orderBy('target_id', 'DESC');
 
+    $query->condition(
+      $query
+        ->orConditionGroup()
+        ->condition('method', $method)
+        ->condition('method', "{$method}|%", 'LIKE')
+        ->condition('method', "%|{$method}", 'LIKE')
+        ->condition('method', "%|{$method}|%", 'LIKE')
+    );
+
+    $query->condition(
+      $query
+        ->orConditionGroup()
+        ->condition('field_name', $field_name)
+        ->condition('field_name', "{$field_name}|%", 'LIKE')
+        ->condition('field_name', "%|{$field_name}", 'LIKE')
+        ->condition('field_name', "%|{$field_name}|%", 'LIKE')
+        ->condition('field_name', "%|%:{$field_name}", 'LIKE')
+        ->condition('field_name', "%|%:{$field_name}|%", 'LIKE')
+    );
+
     $entities = [];
     foreach ($query->execute() as $usage) {
       $target_id_value = !empty($usage->target_id) ? $usage->target_id : $usage->target_id_string;
-      $entities[] = $usage->target_type . '|' . $target_id_value;
+      $entities[] = !$return_as_arrays
+        ? $usage->target_type . '|' . $target_id_value
+        : [
+          'target_type' => $usage->target_type,
+          'target_id' => $target_id_value,
+          'method' => $usage->method,
+          'field_name' => $usage->field_name,
+        ];
     }
     return $entities;
   }
diff --git a/src/EntityUsageInterface.php b/src/EntityUsageInterface.php
index 4d931836fd41ec9b609a149fa10b25b9d9175c46..9626b14093d61c186da1b03dbe9e7bd715ddb427 100644
--- a/src/EntityUsageInterface.php
+++ b/src/EntityUsageInterface.php
@@ -242,14 +242,19 @@ interface EntityUsageInterface {
    *   the plugin id.
    * @param string $field_name
    *   The field name.
+   * @param bool $return_as_arrays
+   *   If TRUE, the target entities are returned as arrays.
    *
-   * @return string[]
-   *   An indexed array of strings where each target entity type and ID are
-   *   concatenated with a "|" character. Will return an empty array if no
-   *   target entities found.
+   * @return array
+   *   An indexed array where each value is:
+   *   - if $return_as_arrays = FALSE: a string of the target entity type and ID
+   *     concatenated with a "|" character.
+   *   - if $return_as_arrays = TRUE: an array with the target_type, target_id,
+   *     method and field_name keys.
+   *   Will return an empty array if no target entities found.
    *
    * @see \Drupal\entity_usage\EntityUsageTrackInterface::getTargetEntities()
    */
-  public function listTargetEntitiesByFieldAndMethod(string|int $source_id, string $source_entity_type_id, string $source_langcode, string|int $source_vid, string $method, string $field_name): array;
+  public function listTargetEntitiesByFieldAndMethod(string|int $source_id, string $source_entity_type_id, string $source_langcode, string|int $source_vid, string $method, string $field_name, bool $return_as_arrays = FALSE): array;
 
 }
diff --git a/src/EntityUsageTrackBase.php b/src/EntityUsageTrackBase.php
index eddcf77840265f865ad365b2a3b2ddb220fa1730..546b875da0ee18e63f7c10d6904618e70e910351 100644
--- a/src/EntityUsageTrackBase.php
+++ b/src/EntityUsageTrackBase.php
@@ -241,13 +241,22 @@ abstract class EntityUsageTrackBase extends PluginBase implements EntityUsageTra
           $this->logTrackingException($e, $source_entity, $field_name);
           continue;
         }
-        // If a field references the same target entity, we record only one
-        // usage.
-        $target_entities = array_unique($target_entities);
+
         foreach ($target_entities as $target_entity) {
-          [$target_type, $target_id] = explode("|", $target_entity);
+          $normalized_target_entity = $this->normalizeTargetEntity($target_entity);
           $source_vid = ($source_entity instanceof RevisionableInterface && $source_entity->getRevisionId()) ? $source_entity->getRevisionId() : 0;
-          $this->usageService->registerUsage($target_id, $target_type, $source_entity->id(), $source_entity->getEntityTypeId(), $source_entity->language()->getId(), $source_vid, $this->pluginId, $field_name);
+          $this
+            ->usageService
+            ->registerUsage(
+              $normalized_target_entity['target_id'],
+              $normalized_target_entity['target_type'],
+              $source_entity->id(),
+              $source_entity->getEntityTypeId(),
+              $source_entity->language()->getId(),
+              $source_vid,
+              $normalized_target_entity['method'] ?? $this->pluginId,
+              $normalized_target_entity['field_name'] ?? $field_name,
+            );
         }
       }
     }
@@ -327,24 +336,64 @@ abstract class EntityUsageTrackBase extends PluginBase implements EntityUsageTra
       $this->logTrackingException($e, $source_entity, $field_name);
     }
 
+    foreach ($current_targets as $key => $current_target) {
+      unset($current_targets[$key]);
+      $current_target = $this->normalizeTargetEntity($current_target);
+      $new_key = implode('|', array_filter([
+        $current_target['target_type'],
+        $current_target['target_id'],
+        $current_target['field_name'] ?? $field_name,
+      ]));
+      $current_targets[$new_key] = $current_target;
+    }
+
     $source_entity_langcode = $source_entity->language()->getId();
     $source_vid = ($source_entity instanceof RevisionableInterface && $source_entity->getRevisionId()) ? $source_entity->getRevisionId() : 0;
-    $original_targets = $this->usageService->listTargetEntitiesByFieldAndMethod($source_entity->id(), $source_entity->getEntityTypeId(), $source_entity_langcode, $source_vid, $this->pluginId, $field_name);
-
-    // If a field references the same target entity, we record only one usage.
-    $original_targets = array_unique($original_targets);
-    $current_targets = array_unique($current_targets);
+    $original_targets = $this
+      ->usageService
+      ->listTargetEntitiesByFieldAndMethod($source_entity->id(), $source_entity->getEntityTypeId(), $source_entity_langcode, $source_vid, $this->pluginId, $field_name, TRUE);
+    foreach ($original_targets as $key => $original_target) {
+      unset($original_targets[$key]);
+      $original_target = $this->normalizeTargetEntity($original_target);
+      $new_key = implode('|', array_filter([
+        $original_target['target_type'],
+        $original_target['target_id'],
+        $original_target['field_name'] ?? $field_name,
+      ]));
+      $original_targets[$new_key] = $original_target;
+    }
 
-    $added_ids = array_diff($current_targets, $original_targets);
-    $removed_ids = array_diff($original_targets, $current_targets);
+    $added_ids = array_diff_key($current_targets, $original_targets);
+    $removed_ids = array_diff_key($original_targets, $current_targets);
 
     foreach ($added_ids as $added_entity) {
-      [$target_type, $target_id] = explode('|', $added_entity);
-      $this->usageService->registerUsage($target_id, $target_type, $source_entity->id(), $source_entity->getEntityTypeId(), $source_entity_langcode, $source_vid, $this->pluginId, $field_name);
+      $this
+        ->usageService
+        ->registerUsage(
+          $added_entity['target_id'],
+          $added_entity['target_type'],
+          $source_entity->id(),
+          $source_entity->getEntityTypeId(),
+          $source_entity_langcode,
+          $source_vid,
+          $added_entity['method'] ?? $this->pluginId,
+          $added_entity['field_name'] ?? $field_name,
+        );
     }
     foreach ($removed_ids as $removed_entity) {
-      [$target_type, $target_id] = explode('|', $removed_entity);
-      $this->usageService->registerUsage($target_id, $target_type, $source_entity->id(), $source_entity->getEntityTypeId(), $source_entity_langcode, $source_vid, $this->pluginId, $field_name, 0);
+      $this
+        ->usageService
+        ->registerUsage(
+          $removed_entity['target_id'],
+          $removed_entity['target_type'],
+          $source_entity->id(),
+          $source_entity->getEntityTypeId(),
+          $source_entity_langcode,
+          $source_vid,
+          $removed_entity['method'] ?? $this->pluginId,
+          $removed_entity['field_name'] ?? $field_name,
+          0
+        );
     }
   }
 
@@ -566,7 +615,7 @@ abstract class EntityUsageTrackBase extends PluginBase implements EntityUsageTra
    * @param string $field_name
    *   The field name that caused the exception.
    */
-  private function logTrackingException(\Exception $e, EntityInterface $source_entity, string $field_name): void {
+  protected function logTrackingException(\Exception $e, EntityInterface $source_entity, string $field_name): void {
     Error::logException(
       $this->entityUsageLogger,
       $e,
@@ -580,4 +629,24 @@ abstract class EntityUsageTrackBase extends PluginBase implements EntityUsageTra
     );
   }
 
+  /**
+   * Normalized a target entity.
+   *
+   * @param string|array $target_entity
+   *   The target array, which can either be a string or an array.
+   *
+   * @return array
+   *   If the target entity was already an array, it is returned unchanged. If
+   *   it was a string, the target entity is returned as an array with the
+   *   target_type and target_id keys.
+   */
+  protected function normalizeTargetEntity(string|array $target_entity): array {
+    return is_array($target_entity)
+      ? $target_entity
+      : array_combine(
+        ['target_type', 'target_id'],
+        explode('|', $target_entity)
+      );
+  }
+
 }
diff --git a/src/EntityUsageTrackInterface.php b/src/EntityUsageTrackInterface.php
index dac7dbb25d72489cd5d1d165bdf9905447c7e06a..b2ace6c3b8f7bb74f56b2a89da9396e99b6dfd14 100644
--- a/src/EntityUsageTrackInterface.php
+++ b/src/EntityUsageTrackInterface.php
@@ -109,10 +109,14 @@ interface EntityUsageTrackInterface extends PluginInspectionInterface {
    * @param \Drupal\Core\Field\FieldItemInterface $item
    *   The field item to get the target entity(ies) from.
    *
-   * @return string[]
-   *   An indexed array of strings where each target entity type and ID are
-   *   concatenated with a "|" character. Will return an empty array if no
-   *   target entity could be retrieved from the received field item value.
+   * @return array<int, string|array{target_type: string, target_id: string|int, field_name?: string, method?: string}>
+   *   An indexed array where each value can be a string or an array:
+   *   - if a string: the target entity type and ID concatenated with a "|"
+   *     character.
+   *   - if an array: an array with the target_type and target_id keys, and
+   *     optionally the method and field_name keys.
+   *   Will return an empty array if no target entities could be retrieved from
+   *   the received field item value.
    */
   public function getTargetEntities(FieldItemInterface $item): array;
 
diff --git a/src/EntityUsageTrackMultipleLoadInterface.php b/src/EntityUsageTrackMultipleLoadInterface.php
index 195261e9bc4479d9e5fb72d0b55dbae856129314..04c6cff62eb689fd4a550736cc96390cb8f5e082 100644
--- a/src/EntityUsageTrackMultipleLoadInterface.php
+++ b/src/EntityUsageTrackMultipleLoadInterface.php
@@ -22,10 +22,14 @@ interface EntityUsageTrackMultipleLoadInterface extends EntityUsageTrackInterfac
    * @param \Drupal\Core\Field\FieldItemListInterface $field
    *   The field to get the target entity(ies) from.
    *
-   * @return string[]
-   *   An indexed array of strings where each target entity type and ID are
-   *   concatenated with a "|" character. Will return an empty array if no
-   *   target entity could be retrieved from the received field item value.
+   * @return array<int, string|array{target_type: string, target_id: string|int, field_name?: string, method?: string}>
+   *   An indexed array where each value can be a string or an array:
+   *   - if a string: the target entity type and ID concatenated with a "|"
+   *     character.
+   *   - if an array: an array with the target_type and target_id keys, and
+   *     optionally the method and field_name keys.
+   *   Will return an empty array if no target entities could be retrieved from
+   *   the received field item value.
    */
   public function getTargetEntitiesFromField(FieldItemListInterface $field): array;
 
diff --git a/src/ParagraphInheritedUsageUpdater.php b/src/ParagraphInheritedUsageUpdater.php
new file mode 100644
index 0000000000000000000000000000000000000000..c1ac2fac354585fbcad8bada8c4105872fa937dc
--- /dev/null
+++ b/src/ParagraphInheritedUsageUpdater.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace Drupal\entity_usage;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Entity\RevisionableInterface;
+use Drupal\Core\Entity\RevisionableStorageInterface;
+use Drupal\paragraphs\ParagraphInterface;
+
+/**
+ * Updates usage info for outer host entities of paragraphs.
+ *
+ * This service complements the "paragraph_inherited" entity usage tracker
+ * plugin, which automatically updates usage when a host entity is modified.
+ *
+ * When a paragraph is updated directly — without triggering an update
+ * on its host entity — this service makes sure the host entity's usage data
+ * is updated accordingly.
+ *
+ * @internal
+ */
+class ParagraphInheritedUsageUpdater {
+
+  /**
+   * ParagraphInheritedUsageUpdater constructor.
+   */
+  public function __construct(
+    protected EntityTypeManagerInterface $entityTypeManager,
+    protected EntityUsageTrackManager $entityUsageTrackManager,
+    protected RecreateTrackingDataForFieldQueuer $recreateTrackingDataForFieldQueuer,
+  ) {}
+
+  /**
+   * A list of paragraph revision IDs for which the usage must be recalculated.
+   *
+   * @var int[]
+   */
+  protected array $paragraphRevisionIds = [];
+
+  /**
+   * Records a paragraph's revision.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to record.
+   */
+  public function queueEntity(EntityInterface $entity): void {
+    if ($entity instanceof ParagraphInterface) {
+      $this->paragraphRevisionIds[$entity->getRevisionId()] = $entity->getRevisionId();
+    }
+  }
+
+  /**
+   * Removes a paragraph revision.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The paragraph revision to remove.
+   */
+  public function removeEntityFromQueue(EntityInterface $entity): void {
+    if ($entity instanceof ParagraphInterface) {
+      unset($this->paragraphRevisionIds[$entity->getRevisionId()]);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function destruct(): void {
+    if (empty($this->paragraphRevisionIds)) {
+      return;
+    }
+    $paragraph_storage = $this
+      ->entityTypeManager
+      ->getStorage('paragraph');
+    assert($paragraph_storage instanceof RevisionableStorageInterface);
+    $paragraph_revisions = $paragraph_storage->loadMultipleRevisions($this->paragraphRevisionIds);
+    foreach ($paragraph_revisions as $paragraph_revision) {
+      assert($paragraph_revision instanceof ParagraphInterface);
+      $outer_host_revisions = $this->getParagraphOuterHostRevisionsFieldItemLists($paragraph_revision);
+      foreach ($outer_host_revisions as $outer_host_revision) {
+        $outer_host_entity = $outer_host_revision->getEntity();
+        $this
+          ->recreateTrackingDataForFieldQueuer
+          ->addRecord(
+            $outer_host_entity->getEntityTypeId(),
+            $outer_host_entity->id(),
+            $outer_host_entity instanceof RevisionableInterface && $outer_host_entity->getRevisionId()
+              ? $outer_host_entity->getRevisionId()
+              : 0,
+            'paragraph_inherited',
+            $outer_host_revision->getName(),
+          );
+      }
+    }
+  }
+
+  /**
+   * Gets the outermost host entity's field item list for a paragraph.
+   *
+   * Walks up the hierarchy until the top-level host is found. The field item
+   * list that starts the chain is returned.
+   *
+   * @param \Drupal\paragraphs\ParagraphInterface $paragraph_revision
+   *   The paragraph revision.
+   *
+   * @return \Drupal\Core\Field\FieldItemListInterface<\Drupal\Core\Field\FieldItemInterface>[]
+   *   An array of field item lists or an empty array if none found.
+   */
+  public function getParagraphOuterHostRevisionsFieldItemLists(ParagraphInterface $paragraph_revision): array {
+    $outer_host_revisions = [];
+
+    $parent_revision_field_item_lists = $this->getParagraphParentRevisionsFieldItemLists($paragraph_revision);
+    while (!empty($parent_revision_field_item_lists)) {
+      foreach ($parent_revision_field_item_lists as $key => $parent_revision_field_item_list) {
+        $parent_revision = $parent_revision_field_item_list->getEntity();
+        if (!$parent_revision instanceof ParagraphInterface) {
+          $outer_host_revisions[] = $parent_revision_field_item_list;
+          unset($parent_revision_field_item_lists[$key]);
+        }
+      }
+
+      if (empty($parent_revision_field_item_lists)) {
+        break;
+      }
+
+      $next_parent_revision_field_item_lists = [];
+      foreach ($parent_revision_field_item_lists as $parent_revision_field_item_list) {
+        $parent_revision_field_item_list_entity = $parent_revision_field_item_list->getEntity();
+        assert($parent_revision_field_item_list_entity instanceof ParagraphInterface);
+        $next_parent_revision_field_item_lists = array_merge(
+          $next_parent_revision_field_item_lists,
+          $this->getParagraphParentRevisionsFieldItemLists($parent_revision_field_item_list_entity),
+        );
+      }
+
+      $parent_revision_field_item_lists = array_filter($next_parent_revision_field_item_lists);
+    }
+
+    return $outer_host_revisions;
+  }
+
+  /**
+   * Gets all field item lists that reference the given paragraph revision.
+   *
+   * @param \Drupal\paragraphs\ParagraphInterface $paragraph_revision
+   *   The paragraph revision.
+   *
+   * @return \Drupal\Core\Field\FieldItemListInterface<\Drupal\Core\Field\FieldItemInterface>[]
+   *   An array of field item lists keyed by the paragraph revision's ID or an
+   *   empty array if none found.
+   */
+  public function getParagraphParentRevisionsFieldItemLists(ParagraphInterface $paragraph_revision): array {
+    $parent_entity_type_id = $paragraph_revision->get('parent_type')->value;
+    $parent_field_name = $paragraph_revision->get('parent_field_name')->value;
+    if (empty($parent_entity_type_id) || empty($parent_field_name)) {
+      return [];
+    }
+
+    $parent_entity_type = $this
+      ->entityTypeManager
+      ->getDefinition($parent_entity_type_id);
+
+    $parent_entity_storage = $this
+      ->entityTypeManager
+      ->getStorage($parent_entity_type->id());
+
+    $parent_revision_ids = $parent_entity_storage
+      ->getQuery()
+      ->accessCheck(FALSE)
+      ->allRevisions()
+      ->condition("{$parent_field_name}.target_revision_id", $paragraph_revision->getRevisionId())
+      ->execute();
+    if (empty($parent_revision_ids)) {
+      return [];
+    }
+
+    $parent_revisions = $parent_entity_type->isRevisionable() && $parent_entity_storage instanceof RevisionableStorageInterface
+      ? $parent_entity_storage->loadMultipleRevisions(array_keys($parent_revision_ids))
+      : $parent_entity_storage->loadMultiple($parent_revision_ids);
+
+    return array_map(function (EntityInterface $parent_revision) use ($parent_field_name) {
+      assert($parent_revision instanceof FieldableEntityInterface);
+      return $parent_revision->get($parent_field_name);
+    }, $parent_revisions);
+  }
+
+}
diff --git a/src/Plugin/EntityUsage/Track/ParagraphInherited.php b/src/Plugin/EntityUsage/Track/ParagraphInherited.php
new file mode 100644
index 0000000000000000000000000000000000000000..5b51270d4f84ff7748e0dcb0393a496c6ca32ee6
--- /dev/null
+++ b/src/Plugin/EntityUsage/Track/ParagraphInherited.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace Drupal\entity_usage\Plugin\EntityUsage\Track;
+
+use Drupal\Core\Entity\TranslatableInterface;
+use Drupal\Core\Field\FieldItemInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\entity_usage\EntityUpdateManagerInterface;
+use Drupal\entity_usage\EntityUsageTrackBase;
+use Drupal\entity_usage\EntityUsageTrackMultipleLoadInterface;
+use Drupal\entity_usage\ParagraphInheritedUsageUpdater;
+use Drupal\paragraphs\ParagraphInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Tracks usage of entities referenced in referenced paragraphs.
+ *
+ * @EntityUsageTrack(
+ *   id = "paragraph_inherited",
+ *   label = @Translation("Paragraph (inherited)"),
+ *   description = @Translation("Tracks entity relationships found inside direct and nested paragraphs as if they belong to the (outer) host entity."),
+ *   field_types = {
+ *     "entity_reference_revisions",
+ *   },
+ *   source_entity_class = "Drupal\Core\Entity\FieldableEntityInterface",
+ * )
+ */
+class ParagraphInherited extends EntityUsageTrackBase implements EntityUsageTrackMultipleLoadInterface {
+
+  /**
+   * The entity update manager.
+   */
+  protected EntityUpdateManagerInterface $entityUpdateManager;
+
+  /**
+   * The paragraph inherited usage updated.
+   */
+  protected ParagraphInheritedUsageUpdater $paragraphInheritedUsageUpdater;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
+    $tracker = parent::create($container, $configuration, $plugin_id, $plugin_definition);
+    $tracker->entityUpdateManager = $container->get('entity_usage.entity_update_manager');
+    $tracker->paragraphInheritedUsageUpdater = $container->get('entity_usage.paragraph_inherited_usage_updater');
+    return $tracker;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTargetEntities(FieldItemInterface $item): array {
+    $parent = $item->getParent();
+    return $parent instanceof FieldItemListInterface
+      ? $this->doGetTargetEntities($parent, $item)
+      : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTargetEntitiesFromField(FieldItemListInterface $field): array {
+    return $this->doGetTargetEntities($field);
+  }
+
+  /**
+   * Gets the target entities.
+   *
+   * @param \Drupal\Core\Field\FieldItemListInterface<\Drupal\Core\Field\FieldItemInterface> $field
+   *   The field.
+   * @param \Drupal\Core\Field\FieldItemInterface|null $field_item
+   *   (optional) The field item.
+   *
+   * @return array<int, string|array{target_type: string, target_id: string|int, field_name?: string, method?: string}>
+   *   An indexed array where each value can be a string or an array:
+   *   - if a string: the target entity type and ID concatenated with a "|"
+   *     character.
+   *   - if an array: an array with the target_type and target_id keys, and
+   *     optionally the method and field_name keys.
+   *   Will return an empty array if no target entities could be retrieved from
+   *   the received field item value.
+   */
+  private function doGetTargetEntities(FieldItemListInterface $field, ?FieldItemInterface $field_item = NULL): array {
+    $target_entities = [];
+
+    $target_type_id = $field->getFieldDefinition()->getSetting('target_type');
+    if ($target_type_id !== 'paragraph') {
+      return $target_entities;
+    }
+
+    /** @var \Drupal\Core\Entity\EntityInterface $parent_entity */
+    $parent_entity = $field->getParent()->getValue();
+    /** @var \Drupal\paragraphs\ParagraphInterface[] $referenced_paragraphs */
+    $referenced_paragraphs = $field_item instanceof FieldItemInterface
+      ? array_filter([$field_item->entity ?? NULL])
+      : $field->referencedEntities();
+    foreach ($referenced_paragraphs as $referenced_paragraph) {
+      // @todo Is this logic correct?
+      if ($referenced_paragraph instanceof TranslatableInterface
+        && $referenced_paragraph->language()->getId() !== $parent_entity->language()->getId()
+        && $referenced_paragraph->hasTranslation($parent_entity->language()->getId())
+      ) {
+        $referenced_paragraph = $referenced_paragraph->getTranslation($parent_entity->language()->getId());
+      }
+
+      foreach ($this->entityUpdateManager->getEnabledPlugins() as $plugin) {
+        $trackable_field_types = $plugin->getApplicableFieldTypes();
+        $fields = array_keys($this->getReferencingFields($referenced_paragraph, $trackable_field_types));
+        foreach ($fields as $field_name) {
+          if (!$referenced_paragraph->hasField($field_name) || $referenced_paragraph->{$field_name}->isEmpty()) {
+            continue;
+          }
+
+          $field_target_entities = [];
+          try {
+            if ($plugin instanceof EntityUsageTrackMultipleLoadInterface) {
+              $field_target_entities = $plugin->getTargetEntitiesFromField($referenced_paragraph->{$field_name});
+            }
+            else {
+              /** @var \Drupal\Core\Field\FieldItemInterface $field_item */
+              foreach ($referenced_paragraph->get($field_name) as $field_item) {
+                $field_target_entities = $plugin->getTargetEntities($field_item);
+              }
+            }
+          }
+          catch (\Exception $e) {
+            $this->logTrackingException($e, $referenced_paragraph, $field_name);
+            continue;
+          }
+
+          foreach ($field_target_entities as $field_target_entity) {
+            $target_entities[] = $this->normalizeTargetEntity($field_target_entity) + [
+              'method' => "{$this->getPluginId()}|{$plugin->getPluginId()}",
+              'field_name' => "{$referenced_paragraph->getRevisionId()}:{$field_name}",
+            ];
+          }
+        }
+      }
+
+      // Since this paragraph has now been processed, we can safely remove it
+      // from the usage updater queue. This prevents the usage tracking from
+      // happening multiple times (for example, when a user saves a paragraph
+      // through the paragraphs field widget).
+      $this
+        ->paragraphInheritedUsageUpdater
+        ->removeEntityFromQueue($referenced_paragraph);
+    }
+
+    foreach ($target_entities as &$target_entity) {
+      $field_name = $field->getName();
+      if ($parent_entity instanceof ParagraphInterface) {
+        $field_name = "{$parent_entity->getRevisionId()}:{$field_name}";
+      }
+      $target_entity['field_name'] = "$field_name|{$target_entity['field_name']}";
+    }
+
+    return $target_entities;
+  }
+
+}