diff --git a/core/lib/Drupal/Core/Cache/ConditionalCacheabilityMetadataBubblingTrait.php b/core/lib/Drupal/Core/Cache/ConditionalCacheabilityMetadataBubblingTrait.php
new file mode 100644
index 0000000..b8c4b33
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/ConditionalCacheabilityMetadataBubblingTrait.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Provides bubble function to apply cacheable dependency to render context.
+ *
+ * This trait should be used with great care. It should only be used by classes
+ * that may be used both inside and outside of a render context.
+ * For example:
+ * - Generating URLs for CLI vs for HTTP responses.
+ * - Serializing/normalizing data for scripts vs for HTTP responses.
+ */
+trait ConditionalCacheabilityMetadataBubblingTrait {
+
+  /**
+   * Bubbles cacheability metadata to the current render context.
+   *
+   * This method does not bubble attachments.
+   *
+   * @param \Drupal\Core\Cache\CacheableDependencyInterface $object
+   *   A cacheable dependency object.
+   */
+  protected function bubble(CacheableDependencyInterface $object) {
+    if ($this->renderer->hasRenderContext()) {
+      $build = [];
+      CacheableMetadata::createFromObject($object)->applyTo($build);
+      $this->renderer->render($build);
+    }
+  }
+
+}
diff --git a/core/modules/image/src/ImageServiceProvider.php b/core/modules/image/src/ImageServiceProvider.php
new file mode 100644
index 0000000..694df8d
--- /dev/null
+++ b/core/modules/image/src/ImageServiceProvider.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\image;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+use Drupal\image\Normalizer\ImageItemNormalizer;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Provides a normalizer service for image field items.
+ */
+class ImageServiceProvider implements ServiceProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) {
+    $modules = $container->getParameter('container.modules');
+    if (isset($modules['serialization'])) {
+      // Add an ImageItem normalizer.
+      $service_definition = new Definition(ImageItemNormalizer::class, [
+        new Reference('entity_type.manager'),
+        new Reference('renderer'),
+      ]);
+      // Priority should be higher than
+      // serializer.normalizer.entity_reference_field_item but lower than
+      // serializer.normalizer.entity_reference_item.hal.
+      $service_definition->addTag('normalizer', ['priority' => 7]);
+      $container->setDefinition('image.normalizer.image_item', $service_definition);
+    }
+  }
+
+}
diff --git a/core/modules/image/src/Normalizer/ImageItemNormalizer.php b/core/modules/image/src/Normalizer/ImageItemNormalizer.php
new file mode 100644
index 0000000..66efd07
--- /dev/null
+++ b/core/modules/image/src/Normalizer/ImageItemNormalizer.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\image\Normalizer;
+
+use Drupal\Core\Cache\ConditionalCacheabilityMetadataBubblingTrait;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\file\Entity\File;
+use Drupal\image\Entity\ImageStyle;
+use Drupal\image\Plugin\Field\FieldType\ImageItem;
+use Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer;
+
+/**
+ * ImageItem normalizer to provide URLs to image styles.
+ */
+class ImageItemNormalizer extends EntityReferenceFieldItemNormalizer {
+
+  use ConditionalCacheabilityMetadataBubblingTrait;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = ImageItem::class;
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs an ImageItemNormalizer object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($object, $format = NULL, array $context = []  ) {
+    $data = parent::normalize($object, $format, $context);
+    if (empty($data['target_id'])) {
+      return $data;
+    }
+    /** @var \Drupal\file\FileInterface $image */
+    $image = $this->entityTypeManager->getStorage('file')->load($data['target_id']);
+    $uri = $image->getFileUri();
+    /** @var \Drupal\image\ImageStyleInterface[] $styles */
+    $styles = $this->entityTypeManager->getStorage('image_style')->loadMultiple();
+    $data['image_styles'] = [];
+    foreach ($styles as $id => $style) {
+      $dimensions = ['width' => $data['width'], 'height' => $data['height']];
+      $style->transformDimensions($dimensions, $uri);
+      $data['image_styles'][$id] = [
+        'url' => file_url_transform_relative($style->buildUrl($uri)),
+        'height' => empty($dimensions['height']) ? NULL : $dimensions['height'],
+        'width' => empty($dimensions['width']) ? NULL : $dimensions['width'],
+      ];
+      $this->bubble($style);
+    }
+    return $data;
+  }
+
+}
diff --git a/core/modules/image/tests/src/Kernel/Normalizer/ImageItemNormalizerTest.php b/core/modules/image/tests/src/Kernel/Normalizer/ImageItemNormalizerTest.php
new file mode 100644
index 0000000..06f32f0
--- /dev/null
+++ b/core/modules/image/tests/src/Kernel/Normalizer/ImageItemNormalizerTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Drupal\Tests\image\Kernel\Normalizer;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Render\RenderContext;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\file\Entity\File;
+use Drupal\image\Entity\ImageStyle;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * @coversDefaultClass \Drupal\image\Normalizer\ImageItemNormalizer
+ * @group image
+ */
+class ImageItemNormalizerTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'entity_test', 'serialization', 'image', 'field', 'user', 'file'];
+
+  /**
+   * The serializer.
+   *
+   * @var \Symfony\Component\Serializer\SerializerInterface
+   */
+  protected $serializer;
+
+  /**
+   * An image for testing.
+   *
+   * @var \Drupal\file\FileInterface
+   */
+  protected $image;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->serializer = \Drupal::service('serializer');
+
+    $this->installEntitySchema('entity_test');
+    $this->installEntitySchema('user');
+    $this->installEntitySchema('file');
+    $this->installConfig('system');
+    $this->installConfig('image');
+    $this->installSchema('file', ['file_usage']);
+
+    FieldStorageConfig::create(array(
+      'entity_type' => 'entity_test',
+      'field_name' => 'image_test',
+      'type' => 'image',
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    ))->save();
+    FieldConfig::create([
+      'entity_type' => 'entity_test',
+      'field_name' => 'image_test',
+      'bundle' => 'entity_test',
+      'settings' => [
+        'file_extensions' => 'jpg',
+      ],
+    ])->save();
+
+    // Change all images style scale effect to upscale.
+    /** @var \Drupal\image\Entity\ImageStyle $image_style */
+    foreach (ImageStyle::loadMultiple() as $image_style) {
+      foreach ($image_style->getEffects() as $effect) {
+        $config = $effect->getConfiguration();
+        $config['data']['upscale'] = TRUE;
+        $effect->setConfiguration($config);
+      }
+      $image_style->save();
+    }
+
+    file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg');
+    $this->image = File::create([
+      'uri' => 'public://example.jpg',
+    ]);
+    $this->image->save();
+  }
+
+  /**
+   * @covers ::normalize
+   */
+  public function testNormalize() {
+    // Create a test entity with the image field set.
+    $original_entity = EntityTest::create();
+    $original_entity->image_test->target_id = $this->image->id();
+    $original_entity->image_test->alt = $alt = $this->randomMachineName();
+    $original_entity->image_test->title = $title = $this->randomMachineName();
+    $original_entity->name->value = $this->randomMachineName();
+    $original_entity->save();
+
+    $entity = clone $original_entity;
+    $context = new RenderContext();
+    $data = $this->container->get('renderer')
+      ->executeInRenderContext($context, function () use ($entity) {
+        return $this->serializer->normalize($entity);
+      });
+    $cacheability = new BubbleableMetadata();
+    $cache_tags = [];
+    /** @var \Drupal\image\ImageStyleInterface $image_style */
+    foreach (ImageStyle::loadMultiple() as $image_style) {
+      $cache_tags = Cache::mergeTags($cache_tags, $image_style->getCacheTags());
+    }
+    $cacheability->setCacheTags($cache_tags);
+
+    $image_style_ids = array_keys($data['image_test'][0]['image_styles']);
+    $expected_image_style_keys = ['large', 'medium', 'thumbnail'];
+    $this->assertEquals($cacheability, $context->pop());
+    $this->assertEquals($expected_image_style_keys, $image_style_ids);
+    $expect_dimensions = [
+      'large' => [
+        'width' => '422',
+        'height' => '480',
+      ],
+      'medium' => [
+        'width' => '194',
+        'height' => '220',
+      ],
+      'thumbnail' => [
+        'width' => '88',
+        'height' => '100',
+      ],
+    ];
+    foreach ($data['image_test'][0]['image_styles'] as $id => $img_info) {
+      $this->assertNotEmpty(strstr($img_info['url'], "files/styles/$id/public/example.jpg"));
+      $this->assertEquals($expect_dimensions[$id]['height'], $img_info['height'], "Style $id matches height.");
+      $this->assertEquals($expect_dimensions[$id]['width'], $img_info['width'], "Style $id matches width.");
+    }
+  }
+
+}
