diff --git a/core/core.services.yml b/core/core.services.yml
index 7abab67..07c0997 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1059,3 +1059,6 @@ services:
     arguments: ['@module_handler']
     tags:
       - { name: mime_type_guesser }
+  plugin.manager.entity.display_component_handler:
+    class: Drupal\Core\Entity\DisplayComponentHandlerPluginManager
+    parent: default_plugin_manager
diff --git a/core/lib/Drupal/Core/Entity/Annotation/DisplayComponent.php b/core/lib/Drupal/Core/Entity/Annotation/DisplayComponent.php
new file mode 100644
index 0000000..e2178c2
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Annotation/DisplayComponent.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Annotation\DisplayComponent.
+ */
+
+namespace Drupal\Core\Entity\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a DisplayComponent annotation object.
+ *
+ * @Annotation
+ */
+class DisplayComponent extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+}
diff --git a/core/lib/Drupal/Core/Entity/DisplayComponentHandlerBase.php b/core/lib/Drupal/Core/Entity/DisplayComponentHandlerBase.php
new file mode 100644
index 0000000..a31d9e6
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/DisplayComponentHandlerBase.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\DisplayComponentHandlerBase.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Component\Plugin\PluginBase;
+
+/**
+ * Provides a base class for DisplayComponentHandler plugins.
+ */
+abstract class DisplayComponentHandlerBase extends PluginBase implements DisplayComponentHandlerInterface {
+
+  /**
+   * The context in which the handler is being used.
+   *
+   * @var array
+   */
+  protected $context;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setContext(array $context) {
+    $this->context = $context;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareDisplayComponents(array &$components, array &$hidden_components) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasElement($name) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageIn($name, array $options) {
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageOut($properties) {
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRenderer($name, array $options) {
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/DisplayComponentHandlerInterface.php b/core/lib/Drupal/Core/Entity/DisplayComponentHandlerInterface.php
new file mode 100644
index 0000000..675fe98
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/DisplayComponentHandlerInterface.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\DisplayComponentHandlerInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * Provides a base class for DisplayComponent plugins.
+ */
+interface DisplayComponentHandlerInterface {
+
+  /**
+   * Checks if the component handler has the passed element.
+   *
+   * @param string $name
+   *   The name of a display component.
+   *
+   * @return bool
+   *   TRUE if the display component handler provides the component.
+   */
+  public function hasElement($name);
+
+  /**
+   * Prepares the element options before set them to storage.
+   *
+   * @param string $name
+   *   The name of a display component.
+   * @param array $options
+   *   The default options for this component.
+   *
+   * @return array
+   *   Massaged component options.
+   */
+  public function massageIn($name, array $options);
+
+  /**
+   * Sets the context for the rendering component.
+   *
+   * @param array $context
+   *   A keyed array containing the current entity display context. The
+   *   following parameters should be set:
+   *   - entity_type: The type of entity.
+   *   - bundle: The entity bundle.
+   *   - view_mode: The entity view mode (default, full).
+   *   - display_context: The type of the display to use (view or form).
+   */
+  public function setContext(array $context);
+
+  /**
+   * Returns the render plugin for the display component.
+   *
+   * @param string $name
+   *   The name of a display component.
+   * @param array $options
+   *   An array of configuration options to instantiate render plugin.
+   *
+   * @return mixed
+   *   The object to render component or null.
+   */
+  public function getRenderer($name, array $options);
+
+  /**
+   * Prepares components when display created.
+   *
+   * @param array $components
+   *   A visible display components.
+   * @param array $hidden_components
+   *   A hidden display components.
+   */
+  public function prepareDisplayComponents(array &$components, array &$hidden_components);
+
+  /**
+   * Prepares the display options after they retrieved from storage.
+   *
+   * @param array $properties
+   *   The entity display properties.
+   *
+   * return @array
+   *   An associative array of the display components with following keys:
+   *   - content: Configured components to render.
+   *   - hidden: Configured components to hide from render.
+   */
+  public function massageOut($properties);
+
+}
diff --git a/core/lib/Drupal/Core/Entity/DisplayComponentHandlerPluginManager.php b/core/lib/Drupal/Core/Entity/DisplayComponentHandlerPluginManager.php
new file mode 100644
index 0000000..87616d8
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/DisplayComponentHandlerPluginManager.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\DisplayComponentHandlerPluginManager.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+/**
+ * Manages entity display component handlers.
+ *
+ * The handlers are typically shared for the whole request. getInstance() holds
+ * the instantiated plugins and only instantiates one of each type.
+ *
+ * @see hook_display_component_handler_info_alter()
+ */
+class DisplayComponentHandlerPluginManager extends DefaultPluginManager {
+
+  /**
+   * The handlers that have already been instantiated by getInstance().
+   *
+   * @var array
+   */
+  protected $plugins = array();
+
+  /**
+   * Constructs a DisplayComponentHandlerPluginManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/DisplayComponent', $namespaces, $module_handler, 'Drupal\Core\Entity\DisplayComponentHandlerInterface' , 'Drupal\Core\Entity\Annotation\DisplayComponent');
+    // @todo Document the alter hook.
+    $this->alterInfo('display_component_handler_info');
+    $this->setCacheBackend($cache_backend, 'display_component_handlers');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInstance(array $options) {
+    $plugin_id = $options['type'];
+
+    if (!isset($this->plugins[$plugin_id]) && !array_key_exists($plugin_id, $this->plugins)) {
+      $this->plugins[$plugin_id] = $this->discovery->getDefinition($plugin_id) ? $this->createInstance($plugin_id) : NULL;
+    }
+
+    return $this->plugins[$plugin_id];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
index 6339c18..cd48873 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
@@ -121,33 +121,6 @@ public function __construct(array $values, $entity_type) {
   /**
    * {@inheritdoc}
    */
-  public function getRenderer($field_name) {
-    if (isset($this->plugins[$field_name])) {
-      return $this->plugins[$field_name];
-    }
-
-    // Instantiate the widget object from the stored display properties.
-    if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) {
-      $widget = $this->pluginManager->getInstance(array(
-        'field_definition' => $definition,
-        'form_mode' => $this->originalMode,
-        // No need to prepare, defaults have been merged in setComponent().
-        'prepare' => FALSE,
-        'configuration' => $configuration
-      ));
-    }
-    else {
-      $widget = NULL;
-    }
-
-    // Persist the widget object.
-    $this->plugins[$field_name] = $widget;
-    return $widget;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function buildForm(ContentEntityInterface $entity, array &$form, FormStateInterface $form_state) {
     // Set #parents to 'top-level' by default.
     $form += array('#parents' => array());
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
index 57041e9..0e2689c 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
@@ -169,33 +169,6 @@ public function __construct(array $values, $entity_type) {
   /**
    * {@inheritdoc}
    */
-  public function getRenderer($field_name) {
-    if (isset($this->plugins[$field_name])) {
-      return $this->plugins[$field_name];
-    }
-
-    // Instantiate the formatter object from the stored display properties.
-    if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) {
-      $formatter = $this->pluginManager->getInstance(array(
-        'field_definition' => $definition,
-        'view_mode' => $this->originalMode,
-        // No need to prepare, defaults have been merged in setComponent().
-        'prepare' => FALSE,
-        'configuration' => $configuration
-      ));
-    }
-    else {
-      $formatter = NULL;
-    }
-
-    // Persist the formatter object.
-    $this->plugins[$field_name] = $formatter;
-    return $formatter;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function build(ContentEntityInterface $entity) {
     $build = $this->buildMultiple(array($entity));
     return $build[0];
diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
index c57b3a6..d02420f 100644
--- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
@@ -79,6 +79,11 @@
   protected $hidden = array();
 
   /**
+   * The renderer objects used for this display, keyed by component name.
+   */
+  protected $renderers = array();
+
+  /**
    * The original view or form mode that was requested (case of view/form modes
    * being configured to fall back to the 'default' display).
    *
@@ -108,6 +113,18 @@
   protected $pluginManager;
 
   /**
+   * A mapping of display elements and its corresponding handler.
+   */
+  protected $handlers;
+
+  /**
+   * The display component handler plugin manager.
+   *
+   * @var \Drupal\Core\Entity\DisplayComponentHandlerPluginManager
+   */
+  protected $handlerManager;
+
+  /**
    * {@inheritdoc}
    */
   public function __construct(array $values, $entity_type) {
@@ -129,6 +146,8 @@ public function __construct(array $values, $entity_type) {
 
     parent::__construct($values, $entity_type);
 
+    $this->handlerManager = \Drupal::service('plugin.manager.entity.display_component_handler');
+
     $this->originalMode = $this->mode;
 
     $this->init();
@@ -210,13 +229,14 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    */
   public function toArray() {
     $properties = parent::toArray();
-    // Do not store options for fields whose display is not set to be
-    // configurable.
-    foreach ($this->getFieldDefinitions() as $field_name => $definition) {
-      if (!$definition->isDisplayConfigurable($this->displayContext)) {
-        unset($properties['content'][$field_name]);
-        unset($properties['hidden'][$field_name]);
-      }
+
+    // Let the component handlers add missing components.
+    if (!$this->handlerManager) {
+      $this->handlerManager = \Drupal::service('plugin.manager.entity.display_component_handler');
+    }
+    $handlers = $this->handlerManager->getDefinitions();
+    foreach (array_keys($handlers) as $type) {
+      $properties = $this->getComponentHandler($type)->massageOut($properties);
     }
 
     return $properties;
@@ -231,40 +251,10 @@ public function toArray() {
    * - or that are not supposed to be configurable.
    */
   protected function init() {
-    // Fill in defaults for extra fields.
-    $context = $this->displayContext == 'view' ? 'display' : $this->displayContext;
-    $extra_fields = \Drupal::entityManager()->getExtraFields($this->targetEntityType, $this->bundle);
-    $extra_fields = isset($extra_fields[$context]) ? $extra_fields[$context] : array();
-    foreach ($extra_fields as $name => $definition) {
-      if (!isset($this->content[$name]) && !isset($this->hidden[$name])) {
-        // Extra fields are visible by default unless they explicitly say so.
-        if (!isset($definition['visible']) || $definition['visible'] == TRUE) {
-          $this->content[$name] = array(
-            'weight' => $definition['weight']
-          );
-        }
-        else {
-          $this->hidden[$name] = TRUE;
-        }
-      }
-    }
-
-    // Fill in defaults for fields.
-    $fields = $this->getFieldDefinitions();
-    foreach ($fields as $name => $definition) {
-      if (!$definition->isDisplayConfigurable($this->displayContext) || (!isset($this->content[$name]) && !isset($this->hidden[$name]))) {
-        $options = $definition->getDisplayOptions($this->displayContext);
-
-        if (!empty($options['type']) && $options['type'] == 'hidden') {
-          $this->hidden[$name] = TRUE;
-        }
-        elseif ($options) {
-          $this->content[$name] = $this->pluginManager->prepareConfiguration($definition->getType(), $options);
-        }
-        // Note: (base) fields that do not specify display options are not
-        // tracked in the display at all, in order to avoid cluttering the
-        // configuration that gets saved back.
-      }
+    // Let the component handlers add missing components.
+    $handlers = $this->handlerManager->getDefinitions();
+    foreach (array_keys($handlers) as $type) {
+      $this->getComponentHandler($type)->prepareDisplayComponents($this->content, $this->hidden);
     }
   }
 
@@ -301,14 +291,15 @@ public function setComponent($name, array $options = array()) {
       $options['weight'] = isset($max) ? $max + 1 : 0;
     }
 
-    // For a field, fill in default options.
-    if ($field_definition = $this->getFieldDefinition($name)) {
-      $options = $this->pluginManager->prepareConfiguration($field_definition->getType(), $options);
+    // Massage in some values.
+    $handler = $this->getComponentHandlerByElementName($name);
+    if ($handler) {
+      $options = $handler->massageIn($name, $options);
     }
 
     $this->content[$name] = $options;
     unset($this->hidden[$name]);
-    unset($this->plugins[$name]);
+    unset($this->renderers[$name]);
 
     return $this;
   }
@@ -410,4 +401,69 @@ public function onDependencyRemoval(array $dependencies) {
     }
   }
 
+  /**
+   * Finds component handler by element name.
+   *
+   * @param string $name
+   *   The element name.
+   *
+   * @return \Drupal\Core\Entity\DisplayComponentHandlerInterface
+   */
+  public function getComponentHandlerByElementName($name) {
+    if (!isset($this->handlers[$name])) {
+      $handlers = $this->handlerManager->getDefinitions();
+      foreach (array_keys($handlers) as $type) {
+        $handler = $this->getComponentHandler($type);
+        if ($handler && $handler->hasElement($name)) {
+          break;
+        }
+        $handler = NULL;
+      }
+      $this->handlers[$name] = $handler;
+    }
+
+    return $this->handlers[$name];
+  }
+
+  /**
+   * Instantiates component handler.
+   *
+   * @param string $type
+   *   The type of component handler (field, extra_field).
+   *
+   * @return \Drupal\Core\Entity\DisplayComponentHandlerInterface
+   */
+  public function getComponentHandler($type) {
+    $handler = $this->handlerManager->getInstance(array('type' => $type));
+    if ($handler) {
+      $handler->setContext(array(
+        'entity_type' => $this->targetEntityType,
+        'bundle' => $this->bundle,
+        'mode' => $this->originalMode,
+        'display_context' => $this->displayContext,
+      ));
+    }
+    return $handler;
+  }
+
+  /**+
+   * {@inheritdoc}
+   */
+  public function getRenderer($name) {
+    if (!isset($this->content[$name])) {
+      return NULL;
+    }
+
+    if (!array_key_exists($name, $this->renderers)) {
+      if ($handler = $this->getComponentHandlerByElementName($name)) {
+        $options = $this->getComponent($name);
+        $this->renderers[$name] = $handler->getRenderer($name, $options);
+      }
+      else {
+        $this->renderers[$name] = NULL;
+      }
+    }
+    return $this->renderers[$name];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DisplayComponent/ExtraFieldDisplayComponentHandler.php b/core/lib/Drupal/Core/Entity/Plugin/DisplayComponent/ExtraFieldDisplayComponentHandler.php
new file mode 100644
index 0000000..6db98e4
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Plugin/DisplayComponent/ExtraFieldDisplayComponentHandler.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Plugin\DisplayComponent\ExtraFieldDisplayComponentHandler.
+ */
+
+namespace Drupal\Core\Entity\Plugin\DisplayComponent;
+
+use Drupal\Core\Entity\DisplayComponentHandlerBase;
+
+/**
+ * Provides a component handler to manage entity extra fields.
+ *
+ * @DisplayComponent(
+ *   id = "extra_field"
+ * )
+ */
+class ExtraFieldDisplayComponentHandler extends DisplayComponentHandlerBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareDisplayComponents(array &$components, array &$hidden_components) {
+    // Fill in defaults for extra fields.
+    $extra_fields = $this->fetchExtraFields();
+    foreach ($extra_fields as $name => $definition) {
+      if (!isset($components[$name]) && !isset($hidden_components[$name])) {
+        // Extra fields are visible by default unless they explicitly say so.
+        if (!isset($definition['visible']) || $definition['visible'] == TRUE) {
+          $components[$name] = array(
+            'weight' => $definition['weight']
+          );
+        }
+        else {
+          $hidden_components[$name] = TRUE;
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasElement($name) {
+    $extra_fields = $this->fetchExtraFields();
+    return isset($extra_fields[$name]);
+  }
+
+  /**
+   * Fetches all the extra fields.
+   */
+  protected function fetchExtraFields() {
+    $context = $this->context['display_context'] == 'view' ? 'display' : $this->context['display_context'];
+    $extra_fields = \Drupal::entityManager()->getExtraFields($this->context['entity_type'], $this->context['bundle']);
+    return isset($extra_fields[$context]) ? $extra_fields[$context] : array();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DisplayComponent/FieldDisplayComponentHandler.php b/core/lib/Drupal/Core/Entity/Plugin/DisplayComponent/FieldDisplayComponentHandler.php
new file mode 100644
index 0000000..013aabd
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Plugin/DisplayComponent/FieldDisplayComponentHandler.php
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Plugin\DisplayComponent\FieldDisplayComponentHandler.
+ */
+
+namespace Drupal\Core\Entity\Plugin\DisplayComponent;
+
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FormatterPluginManager;
+use Drupal\Core\Field\WidgetPluginManager;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Entity\DisplayComponentHandlerBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a component handler to manage entity fields.
+ *
+ * @DisplayComponent(
+ *   id = "field"
+ * )
+ */
+class FieldDisplayComponentHandler extends DisplayComponentHandlerBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The field formatter plugin manager.
+   *
+   * @var \Drupal\Core\Field\FormatterPluginManager
+   */
+  protected $formatterPluginManager;
+
+  /**
+   * The field widget plugin manager.
+   *
+   * @var \Drupal\Core\Field\WidgetPluginManager
+   */
+  protected $widgetPluginManager;
+
+  /**
+   * Constructs a FieldDisplayComponentHandler object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Field\FormatterPluginManager $formatter_plugin_manager
+   *   The field formatter plugin manager.
+   * @param \Drupal\Core\Field\WidgetPluginManager $widget_plugin_manager
+   *   The field widget plugin manager.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, FormatterPluginManager $formatter_plugin_manager, WidgetPluginManager $widget_plugin_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->formatterPluginManager = $formatter_plugin_manager;
+    $this->widgetPluginManager = $widget_plugin_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($configuration, $plugin_id, $plugin_definition,
+      $container->get('plugin.manager.field.formatter'),
+      $container->get('plugin.manager.field.widget')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageIn($name, array $options) {
+    $field_definition = $this->getFieldDefinition($name);
+    if (!isset($field_definition)) {
+      // The field in process of removal from display.
+      return $options;
+    }
+    if ($this->context['display_context'] == 'view') {
+      return $this->formatterPluginManager->prepareConfiguration($field_definition->getType(), $options);
+    }
+    else {
+      return $this->widgetPluginManager->prepareConfiguration($field_definition->getType(), $options);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageOut($properties) {
+    // Do not store options for fields whose display is not set to be
+    // configurable.
+    foreach ($this->getFieldDefinitions() as $field_name => $definition) {
+      if (!$definition->isDisplayConfigurable($this->context['display_context'])) {
+        unset($properties['content'][$field_name]);
+        unset($properties['hidden'][$field_name]);
+      }
+    }
+
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareDisplayComponents(array &$components, array &$hidden_components) {
+    if ($this->context['display_context'] == 'view') {
+      $plugin_manager = $this->formatterPluginManager;
+    }
+    else {
+      $plugin_manager = $this->widgetPluginManager;
+    }
+
+    // Fill in defaults for fields.
+    $fields = $this->getFieldDefinitions();
+    foreach ($fields as $name => $definition) {
+      if (!$definition->isDisplayConfigurable($this->context['display_context']) || (!isset($components[$name]) && !isset($hidden_components[$name]))) {
+        $options = $definition->getDisplayOptions($this->context['display_context']);
+
+        if (!empty($options['type']) && $options['type'] == 'hidden') {
+          $hidden_components[$name] = TRUE;
+        }
+        elseif ($options) {
+          $components[$name] = $plugin_manager->prepareConfiguration($definition->getType(), $options);
+        }
+        // Note: (base) fields that do not specify display options are not
+        // tracked in the display at all, in order to avoid cluttering the
+        // configuration that gets saved back.
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRenderer($name, array $options) {
+    if (isset($options['type']) && ($definition = $this->getFieldDefinition($name))) {
+      if ($this->context['display_context'] == 'view') {
+        $plugin_manager = $this->formatterPluginManager;
+        $mode_key = 'view_mode';
+      }
+      else {
+        $plugin_manager = $this->widgetPluginManager;
+        $mode_key = 'form_mode';
+      }
+
+      return $plugin_manager->getInstance(array(
+        'field_definition' => $definition,
+        $mode_key => $this->context['mode'],
+        // No need to prepare, defaults have been merged when the options were
+        // written in the display.
+        'prepare' => FALSE,
+        'configuration' => $options,
+      ));
+    }
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasElement($name) {
+    $field_definition = $this->getFieldDefinition($name);
+    return isset($field_definition);
+  }
+
+  /**
+   * Returns the field definition of a field.
+   */
+  protected function getFieldDefinition($field_name) {
+    $definitions = $this->getFieldDefinitions();
+    return isset($definitions[$field_name]) ? $definitions[$field_name] : NULL;
+  }
+
+  /**
+   * Returns the definitions of the fields that are candidate for display.
+   */
+  protected function getFieldDefinitions() {
+    $entity_manager = \Drupal::entityManager();
+
+    $entity_type = $this->context['entity_type'];
+    $bundle = $this->context['bundle'];
+    $display_context = $this->context['display_context'];
+    $definitions = $entity_manager->getFieldDefinitions($entity_type, $bundle);
+
+    // The display only cares about fields that specify display options.
+    // Discard base fields that are not rendered through formatters / widgets.
+    return array_filter($definitions, function (FieldDefinitionInterface $definition) use ($display_context) {
+      return $definition->getDisplayOptions($display_context);
+    });
+  }
+
+}
