diff --git a/core/lib/Drupal/Core/Field/FormatterBase.php b/core/lib/Drupal/Core/Field/FormatterBase.php
index a0ed3ea11e71212a43bc5db010bb48dd949bbced..aa415d39cad14517f6c99103fc5c00de83f7e849 100644
--- a/core/lib/Drupal/Core/Field/FormatterBase.php
+++ b/core/lib/Drupal/Core/Field/FormatterBase.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Core\Field;
 
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -88,8 +90,16 @@ public function view(FieldItemListInterface $items, $langcode = NULL) {
     }
     $elements = $this->viewElements($items, $langcode);
 
+    // Field item lists, in particular for computed fields, may carry cacheable
+    // metadata which must be bubbled.
+    if ($items instanceof CacheableDependencyInterface) {
+      (new CacheableMetadata())
+        ->addCacheableDependency($items)
+        ->applyTo($elements);
+    }
+
     // If there are actual renderable children, use #theme => field, otherwise,
-    // let access cacheability metadata pass through for correct bubbling.
+    // let cacheability metadata pass through for correct bubbling.
     if (Element::children($elements)) {
       $entity = $items->getEntity();
       $entity_type = $entity->getEntityTypeId();
diff --git a/core/modules/jsonapi/tests/src/Functional/EntityTestComputedFieldTest.php b/core/modules/jsonapi/tests/src/Functional/EntityTestComputedFieldTest.php
index e3f9a9838dd46f288cafbd045fe5882f82132aca..33c855c8a97ce21519889db0331d9ffcda03c962 100644
--- a/core/modules/jsonapi/tests/src/Functional/EntityTestComputedFieldTest.php
+++ b/core/modules/jsonapi/tests/src/Functional/EntityTestComputedFieldTest.php
@@ -114,6 +114,7 @@ protected function getExpectedDocument() {
           'drupal_internal__id' => 1,
           'computed_string_field' => NULL,
           'computed_test_cacheable_string_field' => 'computed test cacheable string field',
+          'computed_test_cacheable_integer_field' => 0,
         ],
         'relationships' => [
           'computed_reference_field' => [
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php
index 6cf035700278023e5c6cffe144da93b1fc1a394c..9ade6b8344a5b5d9eafb0c0cdeaaa321f9dfa91f 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php
@@ -6,6 +6,7 @@
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\entity_test\Plugin\Field\ComputedReferenceTestFieldItemList;
+use Drupal\entity_test\Plugin\Field\ComputedTestCacheableIntegerItemList;
 use Drupal\entity_test\Plugin\Field\ComputedTestCacheableStringItemList;
 use Drupal\entity_test\Plugin\Field\ComputedTestFieldItemList;
 
@@ -49,12 +50,22 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setSetting('target_type', 'entity_test')
       ->setClass(ComputedReferenceTestFieldItemList::class);
 
+    // Cacheable metadata can either be provided via the field item properties
+    // or via the field item list class directly. Add a computed string field
+    // which does the former and a computed integer field which does the latter.
     $fields['computed_test_cacheable_string_field'] = BaseFieldDefinition::create('computed_test_cacheable_string_item')
       ->setLabel(new TranslatableMarkup('Computed Cacheable String Field Test'))
       ->setComputed(TRUE)
       ->setClass(ComputedTestCacheableStringItemList::class)
       ->setReadOnly(FALSE)
       ->setInternal(FALSE);
+    $fields['computed_test_cacheable_integer_field'] = BaseFieldDefinition::create('integer')
+      ->setLabel(new TranslatableMarkup('Computed Cacheable Integer Field Test'))
+      ->setComputed(TRUE)
+      ->setClass(ComputedTestCacheableIntegerItemList::class)
+      ->setReadOnly(FALSE)
+      ->setInternal(FALSE)
+      ->setDisplayOptions('view', ['weight' => 10]);
 
     return $fields;
   }
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestCacheableIntegerItemList.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestCacheableIntegerItemList.php
new file mode 100644
index 0000000000000000000000000000000000000000..6c9a85b9aed741b34ee481fb9bfe51d606edf8fb
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestCacheableIntegerItemList.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\entity_test\Plugin\Field;
+
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\Cache\CacheableDependencyTrait;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Field\FieldItemList;
+use Drupal\Core\TypedData\ComputedItemListTrait;
+
+/**
+ * Item list class for computed cacheable string field.
+ *
+ * This class sets the cacheable metadata on the field item list directly.
+ *
+ * @see \Drupal\entity_test\Plugin\Field\ComputedTestCacheableStringItemList
+ */
+class ComputedTestCacheableIntegerItemList extends FieldItemList implements CacheableDependencyInterface {
+
+  use CacheableDependencyTrait, ComputedItemListTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function computeValue() {
+    $value = \Drupal::state()->get('entity_test_computed_integer_value', 0);
+    $item = $this->createItem(0, $value);
+    $cacheability = (new CacheableMetadata())
+      ->setCacheContexts(['url.query_args:computed_test_cacheable_integer_field'])
+      ->setCacheTags(['field:computed_test_cacheable_integer_field'])
+      ->setCacheMaxAge(31536000);
+    $this->setCacheability($cacheability);
+    $this->list[0] = $item;
+  }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestCacheableStringItemList.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestCacheableStringItemList.php
index 21aaa9f7b0c7a990d384d78b3c11152f46f69deb..307409babcee5d89bdf0b4ece28fc9c52073bcc0 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestCacheableStringItemList.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestCacheableStringItemList.php
@@ -8,6 +8,10 @@
 
 /**
  * Item list class for computed cacheable string field.
+ *
+ *  This class sets the cacheable metadata on the field item properties.
+ *
+ * @see \Drupal\entity_test\Plugin\Field\ComputedTestCacheableIntegerItemList
  */
 class ComputedTestCacheableStringItemList extends FieldItemList {
 
diff --git a/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestComputedFieldNormalizerTest.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestComputedFieldNormalizerTest.php
index b6c914e32d091d240dc58ff43f4b8b4b093f750c..2090db49aa60b40b0ac424c4363b5e24cf98ab62 100644
--- a/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestComputedFieldNormalizerTest.php
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestComputedFieldNormalizerTest.php
@@ -56,6 +56,12 @@ protected function getExpectedNormalizedEntity() {
         'value' => 'computed test cacheable string field',
       ],
     ];
+    // @see \Drupal\entity_test\Plugin\Field\ComputedTestCacheableIntegerItemList::computeValue().
+    $expected['computed_test_cacheable_integer_field'] = [
+      [
+        'value' => 0,
+      ],
+    ];
 
     $expected['uuid'] = [
       0 => [
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityComputedFieldTest.php b/core/modules/system/tests/src/Functional/Entity/EntityComputedFieldTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..56e939986f7f33b26bc5916d1b388466267dbc3c
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Entity/EntityComputedFieldTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\Tests\system\Functional\Entity;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\State\StateInterface;
+use Drupal\entity_test\Entity\EntityTestComputedField;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests that entities with computed fields work correctly.
+ *
+ * @group Entity
+ */
+class EntityComputedFieldTest extends BrowserTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  protected static $modules = ['entity_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'olivero';
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected StateInterface $state;
+
+  protected function setUp(): void {
+    parent::setUp();
+
+    $this->state = $this->container->get('state');
+  }
+
+  /**
+   * Tests that formatters bubble the cacheable metadata of computed fields.
+   */
+  public function testFormatterComputedFieldCacheableMetadata() {
+    $this->drupalLogin($this->drupalCreateUser(['administer entity_test content']));
+
+    $entity = EntityTestComputedField::create([
+      'name' => 'Test entity with a cacheable, computed field',
+    ]);
+    $entity->save();
+
+    $this->state->set('entity_test_computed_integer_value', 2024);
+    $this->drupalGet($entity->toUrl('canonical')->toString());
+    $field_item_selector = '.field--name-computed-test-cacheable-integer-field .field__item';
+    $this->assertSession()->elementTextEquals('css', $field_item_selector, 2024);
+    $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Contexts', 'url.query_args:computed_test_cacheable_integer_field');
+    $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'field:computed_test_cacheable_integer_field');
+    $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Max-Age', "31536000");
+
+    $this->state->set('entity_test_computed_integer_value', 2025);
+    $this->drupalGet($entity->toUrl('canonical')->toString());
+    $this->assertSession()->elementTextEquals('css', $field_item_selector, 2024);
+    $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Contexts', 'url.query_args:computed_test_cacheable_integer_field');
+    $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'field:computed_test_cacheable_integer_field');
+    $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Max-Age', "31536000");
+
+    Cache::invalidateTags(['field:computed_test_cacheable_integer_field']);
+    $this->drupalGet($entity->toUrl('canonical')->toString());
+    $this->assertSession()->elementTextEquals('css', $field_item_selector, 2025);
+    $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Contexts', 'url.query_args:computed_test_cacheable_integer_field');
+    $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'field:computed_test_cacheable_integer_field');
+    $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Max-Age', "31536000");
+  }
+
+}