diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 60f34c34f0fbe7a9f3663e093dea4aa42176fb7a..63a35eb3775d8d3587ad70fa07ba9add42e060cc 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -244,3 +244,23 @@ entity_view_display.field.number_unformatted:
       label: 'Settings'
       sequence:
         - type: string
+
+entity_view_display.field.uri_link:
+  type: entity_field_view_display_base
+  label: 'URI as link display format settings'
+  mapping:
+    settings:
+      type: sequence
+      label: 'Settings'
+      sequence:
+        - type: string
+
+entity_view_display.field.timestamp_ago:
+  type: entity_field_view_display_base
+  label: 'Timestamp ago display format settings'
+  mapping:
+    settings:
+      type: sequence
+      label: 'Settings'
+      sequence:
+        - type: string
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
new file mode 100644
index 0000000000000000000000000000000000000000..648a17141c70d28aa601bff8871aa598205d00d3
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampAgoFormatter.
+ */
+
+namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin implementation of the 'timestamp' formatter as time ago.
+ *
+ * @FieldFormatter(
+ *   id = "timestamp_ago",
+ *   label = @Translation("Time ago"),
+ *   field_types = {
+ *     "timestamp",
+ *     "created",
+ *     "updated",
+ *   }
+ * )
+ */
+class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The date formatter service.
+   *
+   * @var \Drupal\Core\Datetime\DateFormatter
+   */
+  protected $dateFormatter;
+
+  /**
+   * Constructs a TimestampAgoFormatter object.
+   *
+   * @param string $plugin_id
+   *   The plugin_id for the formatter.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The definition of the field to which the formatter is associated.
+   * @param array $settings
+   *   The formatter settings.
+   * @param string $label
+   *   The formatter label display setting.
+   * @param string $view_mode
+   *   The view mode.
+   * @param array $third_party_settings
+   *   Any third party settings settings.
+   * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
+   *   The date formatter service.
+   */
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
+
+    $this->dateFormatter = $date_formatter;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    // @see \Drupal\Core\Field\FormatterPluginManager::createInstance().
+    return new static(
+      $plugin_id,
+      $plugin_definition,
+      $configuration['field_definition'],
+      $configuration['settings'],
+      $configuration['label'],
+      $configuration['view_mode'],
+      $configuration['third_party_settings'],
+      $container->get('date.formatter')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(FieldItemListInterface $items) {
+    $elements = array();
+
+    foreach ($items as $delta => $item) {
+      if ($item->value) {
+        $updated = $this->t('@time ago', array('@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $item->value)));
+      }
+      else {
+        $updated = $this->t('never');
+      }
+
+      $elements[$delta] = array('#markup' => $updated);
+    }
+
+    return $elements;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
new file mode 100644
index 0000000000000000000000000000000000000000..762d5f38531b8d75799aed424117b713ae9ef80c
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\UriLinkFormatter.
+ */
+
+namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Field\FieldItemListInterface;
+
+/**
+ * Plugin implementation of the 'uri_link' formatter.
+ *
+ * @FieldFormatter(
+ *   id = "uri_link",
+ *   label = @Translation("Link to URI"),
+ *   field_types = {
+ *     "uri",
+ *   }
+ * )
+ */
+class UriLinkFormatter extends FormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(FieldItemListInterface $items) {
+    $elements = array();
+
+    foreach ($items as $delta => $item) {
+      $elements[$delta] = array(
+        '#type' => 'link',
+        '#href' => $item->value,
+        '#title' => $item->value,
+      );
+    }
+
+    return $elements;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index 2e4703e70c5e1b21e05b1d399fb0a20a479d8141..a823cac3ae054c0b2fa755f2dbcd0a115957d72b 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -21,7 +21,8 @@
  *   id = "uri",
  *   label = @Translation("URI"),
  *   description = @Translation("An entity field containing a URI."),
- *   no_ui = TRUE
+ *   no_ui = TRUE,
+ *   default_formatter = "uri_link",
  * )
  */
 class UriItem extends StringItem {
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 7361345b88fef2777803c8c29110c3f351f62caa..1cd23c0ead94a6b15847d497164e8af2536a8180 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -56,28 +56,18 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) {
  */
 function aggregator_theme() {
   return array(
-    'aggregator_feed_source' => array(
-      'variables' => array('aggregator_feed' => NULL, 'view_mode' => NULL),
+    'aggregator_feed' => array(
+      'render element' => 'elements',
       'file' => 'aggregator.theme.inc',
-      'template' => 'aggregator-feed-source',
+      'template' => 'aggregator-feed',
     ),
     'aggregator_block_item' => array(
       'variables' => array('item' => NULL, 'feed' => 0),
       'file' => 'aggregator.theme.inc',
       'template' => 'aggregator-block-item',
     ),
-    'aggregator_summary_items' => array(
-      'variables' => array('summary_items' => NULL, 'source' => NULL),
-      'file' => 'aggregator.theme.inc',
-      'template' => 'aggregator-summary-items',
-    ),
-    'aggregator_summary_item' => array(
-      'variables' => array('aggregator_item' => NULL, 'view_mode' => NULL),
-      'file' => 'aggregator.theme.inc',
-      'template' => 'aggregator-summary-item',
-    ),
     'aggregator_item' => array(
-      'variables' => array('aggregator_item' => NULL, 'view_mode' => NULL),
+      'render element' => 'elements',
       'file' => 'aggregator.theme.inc',
       'template' => 'aggregator-item',
     ),
@@ -85,11 +75,59 @@ function aggregator_theme() {
       'variables' => array('feeds' => NULL),
       'file' => 'aggregator.theme.inc',
     ),
-    'aggregator_page_rss' => array(
-      'variables' => array('feeds' => NULL),
-      'file' => 'aggregator.theme.inc',
+  );
+}
+
+/**
+ * Implements hook_entity_extra_field_info().
+ */
+function aggregator_entity_extra_field_info() {
+  $extra = array();
+
+  $extra['aggregator_feed']['aggregator_feed'] = array(
+    'display' => array(
+      'items' => array(
+        'label' => t('Items'),
+        'description' => t('Items associated with this feed'),
+        'weight' => 0,
+      ),
+      // @todo Move to a formatter at https://www.drupal.org/node/2339917.
+      'image' => array(
+        'label' => t('Image'),
+        'description' => t('The feed image'),
+        'weight' => 2,
+      ),
+      // @todo Move to a formatter at https://drupal.org/node/2149845.
+      'description' => array(
+        'label' => t('Description'),
+        'description' => t('The description of this feed'),
+        'weight' => 3,
+      ),
+      'more_link' => array(
+        'label' => t('More link'),
+        'description' => t('A more link to the feed detail page'),
+        'weight' => 5,
+      ),
+      'feed_icon' => array(
+        'label' => t('Feed icon'),
+        'description' => t('An icon that links to the feed url'),
+        'weight' => 6,
+      ),
     ),
   );
+
+  $extra['aggregator_item']['aggregator_item'] = array(
+    'display' => array(
+      // @todo Move to a formatter at https://drupal.org/node/2149845.
+      'description' => array(
+        'label' => t('Description'),
+        'description' => t('The description of this feed item'),
+        'weight' => 2,
+      ),
+    ),
+  );
+
+  return $extra;
 }
 
 /**
diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml
index 00766ebdbc6cd94a8fdd89717c10a0c9fe683c56..40f1e943daf5d42b107d8c513947dbbacb1dd088 100644
--- a/core/modules/aggregator/aggregator.routing.yml
+++ b/core/modules/aggregator/aggregator.routing.yml
@@ -52,7 +52,7 @@ aggregator.feed_add:
 entity.aggregator_feed.canonical:
   path: '/aggregator/sources/{aggregator_feed}'
   defaults:
-    _content: '\Drupal\aggregator\Controller\AggregatorController::viewFeed'
+    _entity_view: 'aggregator_feed'
     _title_callback: '\Drupal\aggregator\Controller\AggregatorController::feedTitle'
   requirements:
     _permission: 'access news feeds'
diff --git a/core/modules/aggregator/aggregator.theme.inc b/core/modules/aggregator/aggregator.theme.inc
index 605025c10b75f78bd24d8788eae530a6915f423a..e90a79890cb0e0f2dcbc5493f517925b92bca452 100644
--- a/core/modules/aggregator/aggregator.theme.inc
+++ b/core/modules/aggregator/aggregator.theme.inc
@@ -15,31 +15,18 @@
  *
  * @param array $variables
  *   An associative array containing:
- *   - aggregator_item: An individual feed item for display on the aggregator
- *     page.
+ *   - elements: An array of elements to display in view mode.
  */
 function template_preprocess_aggregator_item(&$variables) {
-  $item = $variables['aggregator_item'];
+  $item = $variables['elements']['#aggregator_item'];
 
-  $variables['feed_url'] = check_url($item->getLink());
-  $variables['feed_title'] = String::checkPlain($item->getTitle());
-  $variables['content'] = aggregator_filter_xss($item->getDescription());
-
-  $variables['source_url'] = '';
-  $variables['source_title'] = '';
-  $fid = $item->getFeedId();
-  if (isset($item->ftitle) && $fid !== NULL) {
-    $variables['source_url'] = url('aggregator/sources/' . $fid);
-    $variables['source_title'] = String::checkPlain($item->ftitle);
-  }
-  if (date('Ymd', $item->getPostedTime()) == date('Ymd')) {
-    $variables['source_date'] = t('%ago ago', array('%ago' => \Drupal::service('date.formatter')->formatInterval(REQUEST_TIME - $item->getPostedTime())));
-  }
-  else {
-    $variables['source_date'] = format_date($item->getPostedTime(), 'medium');
+  // Helpful $content variable for templates.
+  foreach (Element::children($variables['elements']) as $key) {
+    $variables['content'][$key] = $variables['elements'][$key];
   }
 
-  $variables['attributes']['class'][] = 'feed-item';
+  $variables['url'] = check_url($item->getLink());
+  $variables['title'] = String::checkPlain($item->label());
 }
 
 /**
@@ -74,104 +61,23 @@ function theme_aggregator_page_opml($variables) {
 }
 
 /**
- * Prepares variables for aggregator summary templates.
- *
- * Default template: aggregator-summary-items.html.twig.
- *
- * @param array $variables
- *   An associative array containing:
- *   - source: A Drupal\aggregator\FeedInterface object representing the feed
- *     source.
- *   - summary_items: An array of feed items.
- */
-function template_preprocess_aggregator_summary_items(&$variables) {
-  $variables['title'] = String::checkPlain($variables['source']->label());
-  $summary_items = array();
-  foreach (Element::children($variables['summary_items']) as $key) {
-    $summary_items[] = $variables['summary_items'][$key];
-  }
-  $variables['summary_list'] = array(
-    '#theme' => 'item_list',
-    '#items' => $summary_items,
-  );
-  $variables['source_url'] = $variables['source']->getUrl();
-}
-
-/**
- * Processes variables for aggregator summary item templates.
+ * Prepares variables for aggregator feed templates.
  *
- * Default template: aggregator-summary-item.html.twig.
+ * Default template: aggregator-feed.html.twig.
  *
  * @param array $variables
  *   An associative array containing:
- *   - aggregator_item: The feed item.
- *   - view_mode: How the item is being displayed.
+ *   - elements: An array of elements to display in view mode.
  */
-function template_preprocess_aggregator_summary_item(&$variables) {
-  $item = $variables['aggregator_item'];
-
-  $variables['url'] = l(String::checkPlain($item->label()), check_url(url($item->getLink(), array('absolute' => TRUE))), array(
-    'attributes' => array(
-      'class' => array('feed-item-url'),
-    ),
-  ));
-  $variables['age'] = array(
-    '#theme' => 'time',
-    '#attributes' => array(
-      'datetime' => format_date($item->getPostedTime(), 'html_datetime', '', 'UTC'),
-      'class' => array('feed-item-age',),
-    ),
-    '#text' => t('%age old', array('%age' => \Drupal::service('date.formatter')->formatInterval(REQUEST_TIME - $item->getPostedTime()))),
-    '#html' => TRUE,
-  );
-}
+function template_preprocess_aggregator_feed(&$variables) {
+  $feed = $variables['elements']['#aggregator_feed'];
 
-/**
- * Prepares variables for aggregator feed source templates.
- *
- * Default template: aggregator-feed-source.html.twig.
- *
- * @param array $variables
- *   An associative array containing:
- *   - aggregator_feed: A Drupal\aggregator\FeedInterface object representing
- *     the feed source.
- */
-function template_preprocess_aggregator_feed_source(&$variables) {
-  $feed = $variables['aggregator_feed'];
-
-  $feed_icon = array(
-    '#theme' => 'feed_icon',
-    '#url' => $feed->getUrl(),
-    '#title' => t('!title feed', array('!title' => $feed->label())),
-  );
-  $variables['source_icon'] = drupal_render($feed_icon);
-
-  if ($feed->getImage() && $feed->label() && $feed->getWebsiteUrl()) {
-    $image = array(
-      '#theme' => 'image',
-      '#path' => $feed->getImage(),
-      '#alt' => $feed->label(),
-    );
-    $variables['source_image'] = l($image, $feed->getWebsiteUrl(), array('html' => TRUE, 'attributes' => array('class' => 'feed-image')));
+  // Helpful $content variable for templates.
+  foreach (Element::children($variables['elements']) as $key) {
+    $variables['content'][$key] = $variables['elements'][$key];
   }
-  else {
-    $variables['source_image'] = '';
-  }
-  $variables['source_description'] = aggregator_filter_xss($feed->getDescription());
-  $variables['source_url'] = check_url(url($feed->getWebsiteUrl(), array('absolute' => TRUE)));
-
-  if ($feed->checked) {
-    $variables['last_checked'] = t('@time ago', array('@time' => \Drupal::service('date.formatter')->formatInterval(REQUEST_TIME - $feed->getLastCheckedTime())));
-  }
-  else {
-    $variables['last_checked'] = t('never');
-  }
-
-  if (\Drupal::currentUser()->hasPermission('administer news feeds')) {
-    $variables['last_checked'] = l($variables['last_checked'], 'admin/config/services/aggregator');
-  }
-
-  $variables['attributes']['class'][] = 'feed-source';
+  $variables['full'] = $variables['elements']['#view_mode'] == 'full';
+  $variables['title'] = String::checkPlain($feed->label());
 }
 
 /**
diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..58e360f4557cc914b2f9b10db929629aac110ccb
--- /dev/null
+++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml
@@ -0,0 +1,31 @@
+id: aggregator_feed.aggregator_feed.default
+targetEntityType: aggregator_feed
+bundle: aggregator_feed
+mode: default
+status: true
+content:
+  items:
+    weight: 0
+  checked:
+    type: timestamp_ago
+    weight: 1
+    settings: {  }
+    third_party_settings: {  }
+    label: inline
+  image:
+    weight: 2
+  description:
+    weight: 3
+  link:
+    type: uri_link
+    weight: 4
+    settings: {  }
+    third_party_settings: {  }
+    label: inline
+  feed_icon:
+    weight: 5
+hidden:
+  more_link: true
+dependencies:
+  module:
+    - aggregator
diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d775c94dbe40cbdc890940ea974746ba77e6430a
--- /dev/null
+++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml
@@ -0,0 +1,22 @@
+id: aggregator_feed.aggregator_feed.summary
+targetEntityType: aggregator_feed
+bundle: aggregator_feed
+mode: summary
+status: true
+content:
+  items:
+    weight: 0
+  more_link:
+    weight: 1
+hidden:
+  link: true
+  checked: true
+  description: true
+  image: true
+  feed_icon: true
+status: true
+dependencies:
+  entity:
+    - core.entity_view_mode.aggregator_feed.summary
+  module:
+    - aggregator
diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bc468928e7299c17949faeca4ee3e168e4a242a1
--- /dev/null
+++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml
@@ -0,0 +1,20 @@
+id: aggregator_item.aggregator_item.summary
+targetEntityType: aggregator_item
+bundle: aggregator_item
+mode: summary
+status: true
+content:
+  timestamp:
+    weight: 0
+hidden:
+  link: true
+  author: true
+  feed: true
+  description: true
+status: true
+dependencies:
+  entity:
+    - core.entity_view_mode.aggregator_item.summary
+  module:
+    - aggregator
+    - entity_reference
diff --git a/core/modules/aggregator/config/install/core.entity_view_mode.aggregator_feed.summary.yml b/core/modules/aggregator/config/install/core.entity_view_mode.aggregator_feed.summary.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c1b4bb8643b21ab1d4b6ca75d0f9e1a2811b6638
--- /dev/null
+++ b/core/modules/aggregator/config/install/core.entity_view_mode.aggregator_feed.summary.yml
@@ -0,0 +1,8 @@
+id: aggregator_feed.summary
+label: Summary
+status: true
+cache: true
+targetEntityType: aggregator_feed
+dependencies:
+  module:
+    - aggregator
diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php
index 1cedb04b39d598ea4c9d7c5f935654587d8dd9b7..6c46b2a9fda5303e101aeaa2b0925a7efb973da3 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -60,26 +60,6 @@ public function feedAdd() {
     return $this->entityFormBuilder()->getForm($feed);
   }
 
-  /**
-   * Displays all the items captured from the particular feed.
-   *
-   * @param \Drupal\aggregator\FeedInterface $aggregator_feed
-   *   The feed for which to display all items.
-   *
-   * @return array
-   *   The rendered list of items for the feed.
-   */
-  public function viewFeed(FeedInterface $aggregator_feed) {
-    $entity_manager = $this->entityManager();
-    $feed_source = $entity_manager->getViewBuilder('aggregator_feed')
-      ->view($aggregator_feed, 'default');
-    // Load aggregator feed item for the particular feed id.
-    $items = $entity_manager->getStorage('aggregator_item')->loadByFeed($aggregator_feed->id(), 20);
-    // Print the feed items.
-    $build = $this->buildPageList($items, $feed_source);
-    return $build;
-  }
-
   /**
    * Builds a listing of aggregator feed items.
    *
@@ -210,35 +190,8 @@ public function sources() {
 
     $feeds = $entity_manager->getStorage('aggregator_feed')->loadMultiple();
 
-    $build = array(
-      '#type' => 'container',
-      '#attributes' => array('class' => array('aggregator-wrapper')),
-      '#sorted' => TRUE,
-    );
-
-    foreach ($feeds as $feed) {
-      // Most recent items:
-      $summary_items = array();
-      $aggregator_summary_items = $this->config('aggregator.settings')
-        ->get('source.list_max');
-      if ($aggregator_summary_items) {
-        $items = $entity_manager->getStorage('aggregator_item')
-          ->loadByFeed($feed->id(), 20);
-        if ($items) {
-          $summary_items = $entity_manager->getViewBuilder('aggregator_item')
-            ->viewMultiple($items, 'summary');
-        }
-      }
-      $feed->url = $this->url('entity.aggregator_feed.canonical', array('aggregator_feed' => $feed->id()));
-      $build[$feed->id()] = array(
-        '#theme' => 'aggregator_summary_items',
-        '#summary_items' => $summary_items,
-        '#source' => $feed,
-        '#cache' => array(
-          'tags' => $feed->getCacheTag(),
-        ),
-      );
-    }
+    $build = $entity_manager->getViewBuilder('aggregator_feed')
+      ->viewMultiple($feeds, 'summary');
     $build['feed_icon'] = array(
       '#theme' => 'feed_icon',
       '#url' => 'aggregator/opml',
diff --git a/core/modules/aggregator/src/Entity/Feed.php b/core/modules/aggregator/src/Entity/Feed.php
index 5a9317a56148e178d0e30eae70e2acc0fbaa7a40..0f2cc781bc4d6373015e88f4cfe4d18664ec8b17 100644
--- a/core/modules/aggregator/src/Entity/Feed.php
+++ b/core/modules/aggregator/src/Entity/Feed.php
@@ -37,6 +37,7 @@
  *     "edit-form" = "entity.aggregator_feed.edit_form",
  *     "delete-form" = "entity.aggregator_feed.delete_form",
  *   },
+ *   field_ui_base_route = "aggregator.admin_overview",
  *   base_table = "aggregator_feed",
  *   fieldable = TRUE,
  *   render_cache = FALSE,
@@ -148,7 +149,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDisplayOptions('form', array(
         'type' => 'string_textfield',
         'weight' => -5,
-      ));
+      ))
+      ->setDisplayConfigurable('form', TRUE);
 
     $fields['langcode'] = BaseFieldDefinition::create('language')
       ->setLabel(t('Language code'))
@@ -161,7 +163,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDisplayOptions('form', array(
         'type' => 'uri',
         'weight' => -3,
-      ));
+      ))
+      ->setDisplayConfigurable('form', TRUE);
 
     $intervals = array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200);
     $period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($intervals, $intervals));
@@ -176,12 +179,19 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDisplayOptions('form', array(
         'type' => 'options_select',
         'weight' => -2,
-      ));
+      ))
+      ->setDisplayConfigurable('form', TRUE);
 
     $fields['checked'] = BaseFieldDefinition::create('timestamp')
       ->setLabel(t('Checked'))
       ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.'))
-      ->setDefaultValue(0);
+      ->setDefaultValue(0)
+      ->setDisplayOptions('view', array(
+        'label' => 'inline',
+        'type' => 'timestamp_ago',
+        'weight' => 1,
+      ))
+      ->setDisplayConfigurable('view', TRUE);
 
     $fields['queued'] = BaseFieldDefinition::create('timestamp')
       ->setLabel(t('Queued'))
@@ -189,8 +199,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDefaultValue(0);
 
     $fields['link'] = BaseFieldDefinition::create('uri')
-      ->setLabel(t('Link'))
-      ->setDescription(t('The link of the feed.'));
+      ->setLabel(t('URL'))
+      ->setDescription(t('The link of the feed.'))
+      ->setDisplayOptions('view', array(
+        'label' => 'inline',
+        'weight' => 4,
+      ))
+      ->setDisplayConfigurable('view', TRUE);
 
     $fields['description'] = BaseFieldDefinition::create('string_long')
       ->setLabel(t('Description'))
diff --git a/core/modules/aggregator/src/Entity/Item.php b/core/modules/aggregator/src/Entity/Item.php
index 3f6cba0aa618b752878ed80899c9e81bcf07a0e6..5df701358d62e7f0741f529222b8a03418c9e358 100644
--- a/core/modules/aggregator/src/Entity/Item.php
+++ b/core/modules/aggregator/src/Entity/Item.php
@@ -58,9 +58,15 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setSetting('unsigned', TRUE);
 
     $fields['fid'] = BaseFieldDefinition::create('entity_reference')
-      ->setLabel(t('Aggregator feed ID'))
-      ->setDescription(t('The ID of the aggregator feed.'))
-      ->setSetting('target_type', 'aggregator_feed');
+      ->setLabel(t('Source feed'))
+      ->setDescription(t('The aggregator feed entity associated with this item.'))
+      ->setSetting('target_type', 'aggregator_feed')
+      ->setDisplayOptions('view', array(
+        'label' => 'hidden',
+        'type' => 'entity_reference_label',
+        'weight' => 0,
+      ))
+      ->setDisplayConfigurable('form', TRUE);
 
     $fields['title'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Title'))
@@ -72,19 +78,34 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['link'] = BaseFieldDefinition::create('uri')
       ->setLabel(t('Link'))
-      ->setDescription(t('The link of the feed item.'));
+      ->setDescription(t('The link of the feed item.'))
+      ->setDisplayOptions('view', array(
+        'type' => 'hidden',
+      ))
+      ->setDisplayConfigurable('view', TRUE);
 
     $fields['author'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Author'))
-      ->setDescription(t('The author of the feed item.'));
+      ->setDescription(t('The author of the feed item.'))
+      ->setDisplayOptions('view', array(
+        'label' => 'hidden',
+        'weight' => 3,
+      ))
+      ->setDisplayConfigurable('view', TRUE);
 
     $fields['description'] = BaseFieldDefinition::create('string_long')
       ->setLabel(t('Description'))
       ->setDescription(t('The body of the feed item.'));
 
     $fields['timestamp'] = BaseFieldDefinition::create('created')
-      ->setLabel(t('Posted timestamp'))
-      ->setDescription(t('Posted date of the feed item, as a Unix timestamp.'));
+      ->setLabel(t('Posted on'))
+      ->setDescription(t('Posted date of the feed item, as a Unix timestamp.'))
+      ->setDisplayOptions('view', array(
+        'label' => 'hidden',
+        'type' => 'timestamp_ago',
+        'weight' => 1,
+      ))
+      ->setDisplayConfigurable('view', TRUE);
 
     // @todo Convert to a real UUID field in https://drupal.org/node/2149851.
     $fields['guid'] = BaseFieldDefinition::create('string_long')
diff --git a/core/modules/aggregator/src/FeedViewBuilder.php b/core/modules/aggregator/src/FeedViewBuilder.php
index 853122fb81de96f790c8d994b981a4f1fb4640a1..26523ab6c44e415cab125b859e35bac9ecf753b3 100644
--- a/core/modules/aggregator/src/FeedViewBuilder.php
+++ b/core/modules/aggregator/src/FeedViewBuilder.php
@@ -7,21 +7,135 @@
 
 namespace Drupal\aggregator;
 
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityViewBuilder;
+use Drupal\Core\Config\Config;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Render controller for aggregator feed items.
  */
 class FeedViewBuilder extends EntityViewBuilder {
 
+  /**
+   * Constructs a new FeedViewBuilder.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type definition.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Config\Config $config
+   *   The 'aggregator.settings' config.
+   */
+  public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, Config $config) {
+    parent::__construct($entity_type, $entity_manager, $language_manager);
+    $this->config = $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('entity.manager'),
+      $container->get('language_manager'),
+      $container->get('config.factory')->get('aggregator.settings')
+    );
+  }
+
   /**
    * {@inheritdoc}
    */
-  protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
-    $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode);
-    $defaults['#theme'] = 'aggregator_feed_source';
-    return $defaults;
+  public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) {
+    parent::buildComponents($build, $entities, $displays, $view_mode, $langcode);
+
+    foreach ($entities as $id => $entity) {
+      $bundle = $entity->bundle();
+      $display = $displays[$bundle];
+
+      if ($display->getComponent('items')) {
+        // When in summary view mode, respect the list_max setting.
+        $limit = $view_mode == 'summary' ? $this->config->get('source.list_max') : 20;
+        // Retrieve the items attached to this feed.
+        $items = $this->entityManager
+          ->getStorage('aggregator_item')
+          ->loadByFeed($entity->id(), $limit);
+
+        $build[$id]['items'] = $this->entityManager
+          ->getViewBuilder('aggregator_item')
+          ->viewMultiple($items, $view_mode, $langcode);
+
+        if ($view_mode == 'full') {
+          // Also add the pager.
+          $build[$id]['pager'] = array('#theme' => 'pager');
+        }
+      }
+
+      if ($display->getComponent('description')) {
+        $build[$id]['description'] = array(
+          '#markup' => aggregator_filter_xss($entity->getDescription()),
+          '#prefix' => '<div class="feed-description">',
+          '#suffix' => '</div>',
+        );
+      }
+
+      if ($display->getComponent('image')) {
+        $image_link = array();
+        // Render the image as link if it is available.
+        $image = $entity->getImage();
+        $label = $entity->label();
+        $link_href = $entity->getWebsiteUrl();
+        if ($image && $label && $link_href) {
+          $link_title = array(
+            '#theme' => 'image',
+            '#path' => $image,
+            '#alt' => $label,
+          );
+          $image_link = array(
+            '#type' => 'link',
+            '#title' => $link_title,
+            '#href' => $link_href,
+            '#options' => array(
+              'attributes' => array('class' => array('feed-image')),
+              'html' => TRUE,
+            ),
+          );
+        }
+        $build[$id]['image'] = $image_link;
+      }
+
+      if ($display->getComponent('feed_icon')) {
+        $build[$id]['feed_icon'] = array(
+          '#theme' => 'feed_icon',
+          '#url' => $entity->getUrl(),
+          '#title' => t('!title feed', array('!title' => $entity->label())),
+        );
+      }
+
+      if ($display->getComponent('more_link')) {
+        $title_stripped = strip_tags($entity->label());
+        $build[$id]['more_link'] = array(
+          '#type' => 'link',
+          '#title' => t('More<span class="visually-hidden"> posts about @title</span>', array(
+            '@title' => $title_stripped,
+          )),
+          '#route_name' => 'entity.aggregator_feed.canonical',
+          '#route_parameters' => array('aggregator_feed' => $entity->id()),
+          '#options' => array(
+            'html' => TRUE,
+            'attributes' => array(
+              'title' => $title_stripped,
+            ),
+          ),
+        );
+      }
+
+    }
   }
 
 }
diff --git a/core/modules/aggregator/src/ItemViewBuilder.php b/core/modules/aggregator/src/ItemViewBuilder.php
index 7d3f919eff3b882e2897329c02ca6178af20bc7f..9207b71161d9f2c7eaae38350b4d0321c3a51f15 100644
--- a/core/modules/aggregator/src/ItemViewBuilder.php
+++ b/core/modules/aggregator/src/ItemViewBuilder.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\aggregator;
 
-use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityViewBuilder;
 
 /**
@@ -18,14 +17,21 @@ class ItemViewBuilder extends EntityViewBuilder {
   /**
    * {@inheritdoc}
    */
-  protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
-    $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode);
+  public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) {
+    parent::buildComponents($build, $entities, $displays, $view_mode, $langcode);
 
-    // Use a different template for the summary view mode.
-    if ($view_mode == 'summary') {
-      $defaults['#theme'] = 'aggregator_summary_item';
+    foreach ($entities as $id => $entity) {
+      $bundle = $entity->bundle();
+      $display = $displays[$bundle];
+
+      if ($display->getComponent('description')) {
+        $build[$id]['description'] = array(
+          '#markup' => aggregator_filter_xss($entity->getDescription()),
+          '#prefix' => '<div class="item-description">',
+          '#suffix' => '</div>',
+        );
+      }
     }
-    return $defaults;
   }
 
 }
diff --git a/core/modules/aggregator/templates/aggregator-feed-source.html.twig b/core/modules/aggregator/templates/aggregator-feed-source.html.twig
deleted file mode 100644
index d492904a0f9546c39bb54f42d81f2755286a1ce9..0000000000000000000000000000000000000000
--- a/core/modules/aggregator/templates/aggregator-feed-source.html.twig
+++ /dev/null
@@ -1,32 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation to present the source of the feed.
- *
- * The contents are rendered below feed listings when browsing source feeds.
- * For example, "example.com/aggregator/sources/1".
- *
- * Available variables:
- * - source_icon: Feed icon linked to the source. Rendered through
- *   feed-icon.html.twig.
- * - source_image: Image set by the feed source.
- * - source_description: Description set by the feed source.
- * - source_url: URL to the feed source.
- * - last_checked: How long ago the feed was checked locally.
- *
- * @see template_preprocess_aggregator_feed_source()
- *
- * @ingroup themeable
- */
-#}
-<div{{ attributes }}>
-  {{ source_icon }}
-  {{ source_image }}
-  <p class="feed-description">{{ source_description }}</p>
-  <dl class="feed-details">
-    <dt class="feed-url">{{ 'URL'|t }}</dt>
-    <dd>{{ source_url }}</dd>
-    <dt class="feed-updated">{{ 'Updated'|t }}</dt>
-    <dd>{{ last_checked }}</dd>
-  </dl>
-</div>
diff --git a/core/modules/aggregator/templates/aggregator-feed.html.twig b/core/modules/aggregator/templates/aggregator-feed.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..1876c453f41cc882a1b4fce46419763558dceb2f
--- /dev/null
+++ b/core/modules/aggregator/templates/aggregator-feed.html.twig
@@ -0,0 +1,31 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present an aggregator feed.
+ *
+ * The contents are rendered above feed listings when browsing source feeds.
+ * For example, "example.com/aggregator/sources/1".
+ *
+ * Available variables:
+ * - title: Title of the feed item.
+ * - content: All field items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given element.
+ *
+ * @see template_preprocess_aggregator_feed()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes.addClass('aggregator-feed') }}>
+
+  {{ title_prefix }}
+  {% if not full %}
+    <h2{{ title_attributes }}>{{ title }}</h2>
+  {% endif %}
+  {{ title_suffix }}
+
+  {{ content }}
+
+</div>
diff --git a/core/modules/aggregator/templates/aggregator-item.html.twig b/core/modules/aggregator/templates/aggregator-item.html.twig
index 8c23106fbd6c12df75f07e780ac284ebaed12df2..8f31937e28e033f16478d0f7feacc74d792ec38d 100644
--- a/core/modules/aggregator/templates/aggregator-item.html.twig
+++ b/core/modules/aggregator/templates/aggregator-item.html.twig
@@ -4,33 +4,25 @@
  * Default theme implementation to present a feed item in an aggregator page.
  *
  * Available variables:
- * - feed_url: URL to the originating feed item.
- * - feed_title: Title of the feed item.
- * - source_url: Link to the local source section.
- * - source_title: Title of the remote source.
- * - source_date: Date the feed was posted on the remote source.
- * - content: Feed item content.
+ * - url: URL to the originating feed item.
+ * - title: Title of the feed item.
+ * - content: All field items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given element.
  *
  * @see template_preprocess_aggregator_item()
  *
  * @ingroup themeable
  */
 #}
-<div{{ attributes }}>
+<div{{ attributes.addClass('aggregator-item') }}>
+  {{ title_prefix }}
   <h3 class="feed-item-title">
-    <a href="{{ feed_url }}">{{ feed_title }}</a>
+    <a href="{{ url }}">{{ title }}</a>
   </h3>
+  {{ title_suffix }}
 
-  <div class="feed-item-meta">
-    {% if source_url %}
-      <a href="{{ source_url }}" class="feed-item-source">{{ source_title }}</a> -
-    {% endif %}
-    <span class="feed-item-date">{{ source_date }}</span>
-  </div>
+  {{ content }}
 
-  {% if content %}
-    <div class="feed-item-body">
-      {{ content }}
-    </div>
-  {% endif %}
 </div>
diff --git a/core/modules/aggregator/templates/aggregator-summary-item.html.twig b/core/modules/aggregator/templates/aggregator-summary-item.html.twig
deleted file mode 100644
index 3cb227908245181376ececc0a9202f2e6267cfaf..0000000000000000000000000000000000000000
--- a/core/modules/aggregator/templates/aggregator-summary-item.html.twig
+++ /dev/null
@@ -1,15 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation for a single feed in a list of feed items.
- *
- * Available variables:
- * - url: URL of item.
- * - age: Age of the item.
- *
- * @see template_preprocess_aggregator_summary_item()
- *
- * @ingroup themeable
- */
-#}
-{{ url }} {{ age }}
diff --git a/core/modules/aggregator/templates/aggregator-summary-items.html.twig b/core/modules/aggregator/templates/aggregator-summary-items.html.twig
deleted file mode 100644
index a00b8dc3d2b21feb778b114e3e458e536737402e..0000000000000000000000000000000000000000
--- a/core/modules/aggregator/templates/aggregator-summary-items.html.twig
+++ /dev/null
@@ -1,23 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation to present feeds as list items.
- *
- * Each iteration generates a single feed source.
- *
- * Available variables:
- * - title: Title of the feed.
- * - summary_list: Unordered list of linked feed items generated through
- *   theme_item_list().
- * - source_url: URL to the local source.
- *
- * @see template_preprocess_aggregator_summary_items()
- *
- * @ingroup themeable
- */
-#}
-<h3>{{ title }}</h3>
-{{ summary_list }}
-<div class="links">
-  <a href="{{ source_url }}">{% trans %}More<span class="visually-hidden"> posts about {{ title|placeholder }}</span>{% endtrans %}</a>
-</div>