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..51c25fa
--- /dev/null
+++ b/core/modules/image/src/ImageServiceProvider.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\image;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+use Drupal\image\Normalizer\ImageItemHalNormalizer;
+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' => 9]);
+      $container->setDefinition('image.normalizer.image_item', $service_definition);
+    }
+    if (isset($modules['hal'])) {
+      // Add an ImageItem normalizer.
+      $service_definition = new Definition(ImageItemHalNormalizer::class, [
+        new Reference('rest.link_manager'),
+        new Reference('serializer.entity_resolver'),
+        new Reference('entity_type.manager'),
+        new Reference('renderer'),
+      ]);
+      // Priority should be higher than
+      // serializer.normalizer.entity_reference_item.hal which is 10.
+      // Priority of 20 gives the ability to have other normalizers between this
+      // one and serializer.normalizer.entity_reference_item.hal.
+      $service_definition->addTag('normalizer', ['priority' => 20]);
+      $container->setDefinition('image.normalizer.hal.image_item', $service_definition);
+    }
+  }
+
+}
diff --git a/core/modules/image/src/Normalizer/ImageItemHalNormalizer.php b/core/modules/image/src/Normalizer/ImageItemHalNormalizer.php
new file mode 100644
index 0000000..905faba
--- /dev/null
+++ b/core/modules/image/src/Normalizer/ImageItemHalNormalizer.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Drupal\image\Normalizer;
+
+use Drupal\Core\Cache\ConditionalCacheabilityMetadataBubblingTrait;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\hal\Normalizer\EntityReferenceItemNormalizer;
+use Drupal\image\Plugin\Field\FieldType\ImageItem;
+use Drupal\rest\LinkManager\LinkManagerInterface;
+use Drupal\serialization\EntityResolver\EntityResolverInterface;
+
+/**
+ * ImageItem HAL normalizer to provide URLs to image styles.
+ */
+class ImageItemHalNormalizer extends EntityReferenceItemNormalizer {
+
+  use ConditionalCacheabilityMetadataBubblingTrait;
+
+  use ImageItemNormalizerTrait;
+
+  /**
+   * 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 ImageItemHalNormalizer object.
+   *
+   * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager
+   *   The hypermedia link manager.
+   * @param \Drupal\serialization\EntityResolver\EntityResolverInterface $entity_Resolver
+   *   The entity resolver.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(LinkManagerInterface $link_manager, EntityResolverInterface $entity_Resolver, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer) {
+    parent::__construct($link_manager, $entity_Resolver);
+    $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['_embedded'])) {
+      $field_key = array_keys($data['_embedded'])[0];
+      $this->addImageStyles($object, $data['_embedded'][$field_key][0]);
+    }
+    return $data;
+  }
+
+}
diff --git a/core/modules/image/src/Normalizer/ImageItemNormalizer.php b/core/modules/image/src/Normalizer/ImageItemNormalizer.php
new file mode 100644
index 0000000..60dbf55
--- /dev/null
+++ b/core/modules/image/src/Normalizer/ImageItemNormalizer.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\image\Normalizer;
+
+use Drupal\Core\Cache\ConditionalCacheabilityMetadataBubblingTrait;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Render\RendererInterface;
+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;
+
+  use ImageItemNormalizerTrait;
+
+  /**
+   * 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;
+    }
+    $this->addImageStyles($object, $data);
+    return $data;
+  }
+
+}
diff --git a/core/modules/image/src/Normalizer/ImageItemNormalizerTrait.php b/core/modules/image/src/Normalizer/ImageItemNormalizerTrait.php
new file mode 100644
index 0000000..d8cba94
--- /dev/null
+++ b/core/modules/image/src/Normalizer/ImageItemNormalizerTrait.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\image\Normalizer;
+
+use Drupal\image\Plugin\Field\FieldType\ImageItem;
+
+/**
+ * A trait for providing ItemItem normalizing methods.
+ */
+trait ImageItemNormalizerTrait {
+
+  /**
+   * Adds image style information to normalized ImageItem field data.
+   *
+   * @param \Drupal\image\Plugin\Field\FieldType\ImageItem $item
+   *   The image field item.
+   * @param array $normalization
+   *   The image field normalization to add image style information to.
+   */
+  protected function addImageStyles(ImageItem $item, array &$data) {
+    /** @var \Drupal\file\FileInterface $image */
+    if ($image = $this->entityTypeManager->getStorage('file')->load($item->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' => $item->width, 'height' => $item->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);
+      }
+    }
+  }
+
+}
diff --git a/core/modules/image/tests/src/Kernel/Normalizer/ImageItemHalNormalizerTest.php b/core/modules/image/tests/src/Kernel/Normalizer/ImageItemHalNormalizerTest.php
new file mode 100644
index 0000000..cf439d1
--- /dev/null
+++ b/core/modules/image/tests/src/Kernel/Normalizer/ImageItemHalNormalizerTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\Tests\image\Kernel\Normalizer;
+
+/**
+ * @coversDefaultClass \Drupal\image\Normalizer\ImageItemHalNormalizer
+ * @group image
+ */
+class ImageItemHalNormalizerTest extends ImageItemNormalizerBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $format = 'hal_json';
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'entity_test', 'serialization', 'image', 'field', 'user', 'file', 'hal', 'rest'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNormalizedImageStyles(array $normalization) {
+    return array_pop($normalization['_embedded'])[0]['image_styles'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheability() {
+    $cacheability = parent::getExpectedCacheability();
+    return $cacheability->setCacheContexts(['url.site']);
+  }
+
+}
diff --git a/core/modules/image/tests/src/Kernel/Normalizer/ImageItemNormalizerBase.php b/core/modules/image/tests/src/Kernel/Normalizer/ImageItemNormalizerBase.php
new file mode 100644
index 0000000..07fac56
--- /dev/null
+++ b/core/modules/image/tests/src/Kernel/Normalizer/ImageItemNormalizerBase.php
@@ -0,0 +1,168 @@
+<?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;
+
+/**
+ * Base class for ItemItemNormalizer testing.
+ */
+abstract class ImageItemNormalizerBase 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;
+
+
+  /**
+   * The format to use in normalization.
+   *
+   * @var string
+   */
+  protected $format;
+
+  /**
+   * {@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();
+
+    // Set upscale to TRUE in all image style scale effects.
+    /** @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, $this->format);
+      });
+
+    $normalized_image_styles = $this->getNormalizedImageStyles($data);
+    $this->assertEquals($this->getExpectedCacheability(), $context->pop());
+    $expect_dimensions = [
+      'large' => [
+        'width' => '422',
+        'height' => '480',
+      ],
+      'medium' => [
+        'width' => '194',
+        'height' => '220',
+      ],
+      'thumbnail' => [
+        'width' => '88',
+        'height' => '100',
+      ],
+    ];
+    $this->assertEquals(array_keys($expect_dimensions), array_keys($normalized_image_styles));
+
+    foreach ($normalized_image_styles as $image_style_id => $image_style_dimensions) {
+      $this->assertContains("files/styles/$image_style_id/public/example.jpg", $image_style_dimensions['url']);
+      $this->assertEquals($expect_dimensions[$image_style_id]['height'], $image_style_dimensions['height'], "Style $image_style_id matches height.");
+      $this->assertEquals($expect_dimensions[$image_style_id]['width'], $image_style_dimensions['width'], "Style $image_style_id matches width.");
+    }
+  }
+
+  /**
+   * Gets normalized image styles from normalized entity.
+   *
+   * @param array $normalization
+   *   The normalized entity.
+   *
+   * @return array
+   *   The normalized image styles.
+   */
+  abstract protected function getNormalizedImageStyles(array $normalization);
+
+  /**
+   * Gets the expected cacheability metadata.
+   *
+   * @return \Drupal\Core\Render\BubbleableMetadata
+   *   The cacheability metadata.
+   */
+  protected function getExpectedCacheability() {
+    $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);
+    return $cacheability;
+  }
+
+}
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..1224370
--- /dev/null
+++ b/core/modules/image/tests/src/Kernel/Normalizer/ImageItemNormalizerTest.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Tests\image\Kernel\Normalizer;
+
+/**
+ * @coversDefaultClass \Drupal\image\Normalizer\ImageItemNormalizer
+ * @group image
+ */
+class ImageItemNormalizerTest extends ImageItemNormalizerBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $format = 'json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNormalizedImageStyles(array $normalization) {
+    return $normalization['image_test'][0]['image_styles'];
+  }
+
+}
