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 0000000..a6522e9
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\UriFormatter.
+ */
+
+namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
+
+use Drupal\Component\Utility\String;
+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,
+        // @todo Make this configurable?
+        '#options' => array('absolute' => TRUE),
+      );
+    }
+
+    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 dbef995..f67eb9f 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 64eb275..024a36b 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,10 +75,6 @@ function aggregator_theme() {
       'variables' => array('feeds' => NULL),
       'file' => 'aggregator.theme.inc',
     ),
-    'aggregator_page_rss' => array(
-      'variables' => array('feeds' => NULL),
-      'file' => 'aggregator.theme.inc',
-    ),
   );
 }
 
@@ -107,6 +93,66 @@ function aggregator_permission() {
 }
 
 /**
+ * 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 after https://drupal.org/node/2226493 which
+      //   provides the TimestampFormatter is in.
+      'checked' => array(
+        'label' => t('Last checked'),
+        'description' => t('When this feed was last checked'),
+        'weight' => 1,
+      ),
+      '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,
+      ),
+    ),
+  );
+
+  $extra['aggregator_item']['aggregator_item'] = array(
+    'display' => array(
+      'feed' => array(
+        'label' => t('Source feed'),
+        'description' => t('The aggregator feed entity associated with this item'),
+        'weight' => 0,
+      ),
+      // @todo Move to a formatter after https://drupal.org/node/2226493 which
+      //   provides the TimestampFormatter is in.
+      'timestamp' => array(
+        'label' => t('Date'),
+        'description' => t('Posted date of the feed as time ago'),
+        'weight' => 1,
+      ),
+      // @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;
+}
+
+/**
  * Implements hook_cron().
  *
  * Queues news feeds for updates once their refresh interval has elapsed.
diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml
index 00766eb..40f1e94 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 1335ee6..53ab9fd 100644
--- a/core/modules/aggregator/aggregator.theme.inc
+++ b/core/modules/aggregator/aggregator.theme.inc
@@ -15,30 +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['url'] = check_url($item->getLink());
+  $variables['title'] = String::checkPlain($item->label());
   $variables['attributes']['class'][] = 'feed-item';
 }
 
@@ -74,101 +62,45 @@ function theme_aggregator_page_opml($variables) {
 }
 
 /**
- * Prepares variables for aggregator summary templates.
+ * Prepares variables for aggregator feed templates.
  *
- * Default template: aggregator-summary-items.html.twig.
+ * Default template: aggregator-feed.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.
+ *   - elements: An array of elements to display in view mode.
  */
-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.
- *
- * Default template: aggregator-summary-item.html.twig.
- *
- * @param array $variables
- *   An associative array containing:
- *   - aggregator_item: The feed item.
- *   - view_mode: How the item is being displayed.
- */
-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' => 'datetime',
-    '#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')));
-  }
-  else {
-    $variables['source_image'] = '';
+  // Helpful $content variable for templates.
+  foreach (Element::children($variables['elements']) as $key) {
+    $variables['content'][$key] = $variables['elements'][$key];
   }
-  $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())));
+  $variables['full'] = $variables['elements']['#view_mode'] == 'full';
+  $variables['title'] = String::checkPlain($feed->label());
+  if ($variables['full']) {
+    $variables['link'] = array(
+      '#theme' => 'feed_icon',
+      '#url' => $feed->getUrl(),
+      '#title' => t('!title feed', array('!title' => $feed->label())),
+    );
   }
   else {
-    $variables['last_checked'] = t('never');
-  }
-
-  if (\Drupal::currentUser()->hasPermission('administer news feeds')) {
-    $variables['last_checked'] = l($variables['last_checked'], 'admin/config/services/aggregator');
+    $title_stripped = strip_tags($feed->label());
+    $variables['link'] = array(
+      '#type' => 'link',
+      '#title' => t('More<span class="visually-hidden"> posts about @title</span>', array(
+        '@title' => $title_stripped,
+      )),
+      '#href' => 'aggregator/sources/' . $feed->id(),
+      '#options' => array(
+        'html' => TRUE,
+        'attributes' => array(
+          'title' => $title_stripped,
+        ),
+      ),
+    );
   }
 
   $variables['attributes']['class'][] = 'feed-source';
diff --git a/core/modules/aggregator/config/install/entity.view_display.aggregator_feed.aggregator_feed.summary.yml b/core/modules/aggregator/config/install/entity.view_display.aggregator_feed.aggregator_feed.summary.yml
new file mode 100644
index 0000000..3097653
--- /dev/null
+++ b/core/modules/aggregator/config/install/entity.view_display.aggregator_feed.aggregator_feed.summary.yml
@@ -0,0 +1,18 @@
+id: aggregator_feed.aggregator_feed.summary
+targetEntityType: aggregator_feed
+bundle: aggregator_feed
+mode: summary
+content:
+  items:
+    weight: 0
+hidden:
+  link: true
+  checked: true
+  description: true
+  image: true
+status: true
+dependencies:
+  entity:
+    - entity.view_mode.aggregator_feed.summary
+  module:
+    - aggregator
diff --git a/core/modules/aggregator/config/install/entity.view_display.aggregator_item.aggregator_item.summary.yml b/core/modules/aggregator/config/install/entity.view_display.aggregator_item.aggregator_item.summary.yml
new file mode 100644
index 0000000..dcbd77c
--- /dev/null
+++ b/core/modules/aggregator/config/install/entity.view_display.aggregator_item.aggregator_item.summary.yml
@@ -0,0 +1,18 @@
+id: aggregator_item.aggregator_item.summary
+targetEntityType: aggregator_item
+bundle: aggregator_item
+mode: summary
+content:
+  timestamp:
+    weight: 0
+hidden:
+  link: true
+  author: true
+  feed: true
+  description: true
+status: true
+dependencies:
+  entity:
+    - entity.view_mode.aggregator_item.summary
+  module:
+    - aggregator
diff --git a/core/modules/aggregator/config/install/entity.view_mode.aggregator_feed.summary.yml b/core/modules/aggregator/config/install/entity.view_mode.aggregator_feed.summary.yml
new file mode 100644
index 0000000..c1b4bb8
--- /dev/null
+++ b/core/modules/aggregator/config/install/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 1cedb04..6c46b2a 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -61,26 +61,6 @@ public function feedAdd() {
   }
 
   /**
-   * 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.
    *
    * @param \Drupal\aggregator\ItemInterface[] $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 d70ec7d..781d0f2 100644
--- a/core/modules/aggregator/src/Entity/Feed.php
+++ b/core/modules/aggregator/src/Entity/Feed.php
@@ -35,6 +35,7 @@
  *     "canonical" = "entity.aggregator_feed.canonical",
  *     "edit-form" = "entity.aggregator_feed.edit_form",
  *     "delete-form" = "entity.aggregator_feed.delete_form",
+ *     "admin-form" = "aggregator.admin_overview",
  *   },
  *   base_table = "aggregator_feed",
  *   fieldable = TRUE,
@@ -147,7 +148,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDisplayOptions('form', array(
         'type' => 'string',
         'weight' => -5,
-      ));
+      ))
+      ->setDisplayConfigurable('form', TRUE);
 
     $fields['langcode'] = BaseFieldDefinition::create('language')
       ->setLabel(t('Language code'))
@@ -160,7 +162,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));
@@ -175,7 +178,8 @@ 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'))
@@ -188,8 +192,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 f41080a5..9995f5c 100644
--- a/core/modules/aggregator/src/Entity/Item.php
+++ b/core/modules/aggregator/src/Entity/Item.php
@@ -71,11 +71,20 @@ 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'))
diff --git a/core/modules/aggregator/src/FeedViewBuilder.php b/core/modules/aggregator/src/FeedViewBuilder.php
index 853122f..73e6a96 100644
--- a/core/modules/aggregator/src/FeedViewBuilder.php
+++ b/core/modules/aggregator/src/FeedViewBuilder.php
@@ -7,8 +7,14 @@
 
 namespace Drupal\aggregator;
 
+use Drupal\Core\Datetime\DateFormatter;
 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.
@@ -16,12 +22,113 @@
 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\Datetime\Date $date_formatter
+   *    The date formatter service.
+   */
+  public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, Config $config, DateFormatter $date_formatter) {
+    parent::__construct($entity_type, $entity_manager, $language_manager);
+    $this->config = $config;
+    $this->dateFormatter = $date_formatter;
+  }
+
+  /**
+   * {@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'),
+      $container->get('date.formatter')
+    );
+  }
+
+  /**
    * {@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('checked')) {
+        // Render the checked timestamp as time ago.
+        $last_checked = $entity->getLastCheckedTime();
+        if ($last_checked) {
+          $updated = t('@time ago', array('@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $last_checked)));
+        }
+        else {
+          $updated = t('never');
+        }
+        $build[$id]['checked'] = array(
+          '#markup' => t('<em>Updated:</em> @updated', array('@updated' => $updated)),
+          '#prefix' => '<div class="feed-updated">',
+          '#suffix' => '</div>',
+        );
+      }
+
+      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;
+      }
+    }
   }
 
 }
diff --git a/core/modules/aggregator/src/ItemViewBuilder.php b/core/modules/aggregator/src/ItemViewBuilder.php
index 7d3f919..d0d2024 100644
--- a/core/modules/aggregator/src/ItemViewBuilder.php
+++ b/core/modules/aggregator/src/ItemViewBuilder.php
@@ -7,8 +7,14 @@
 
 namespace Drupal\aggregator;
 
+use Drupal\Component\Utility\String;
+use Drupal\Core\Datetime\DateFormatter;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityViewBuilder;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Render controller for aggregator feed items.
@@ -16,16 +22,102 @@
 class ItemViewBuilder extends EntityViewBuilder {
 
   /**
+   * Constructs a new ItemViewBuilder.
+   *
+   * @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\Datetime\Date $date_formatter
+   *    The date formatter service.
+   */
+  public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, DateFormatter $date_formatter) {
+    parent::__construct($entity_type, $entity_manager, $language_manager);
+    $this->dateFormatter = $date_formatter;
+  }
+
+  /**
    * {@inheritdoc}
    */
-  protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
-    $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode);
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('entity.manager'),
+      $container->get('language_manager'),
+      $container->get('date.formatter')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  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('feed')) {
+        // Retrieve the feed.
+        $feed = $this->entityManager
+          ->getStorage('aggregator_feed')
+          ->load($entity->getFeedId());
+
+        // Render the feed title as well a link to it.
+        $build[$id]['feed'] = array(
+          '#type' => 'link',
+          '#title' => $feed->label(),
+          '#href' => 'aggregator/sources/' . $feed->id(),
+          '#options' => array(
+            'attributes' => array('class' => array('feed-item-source')),
+          ),
+          '#prefix' => '<div class="feed-source-meta">',
+          '#suffix' => '</div>',
+        );
+      }
+
+      if ($display->getComponent('timestamp')) {
+        if ($view_mode == 'summary') {
+          // Always render the timestamp as time ago on summary.
+          $build[$id]['timestamp'] = array(
+            '#theme' => 'datetime',
+            '#attributes' => array(
+              'datetime' => $this->dateFormatter->format($entity->getPostedTime(), 'html_datetime', '', 'UTC'),
+              'class' => array('feed-item-age'),
+            ),
+            '#text' => t('%age old', array('%age' => $this->dateFormatter->formatInterval(REQUEST_TIME - $entity->getPostedTime()))),
+            '#html' => TRUE,
+          );
+        }
+        else {
+          // On full mode only display as time ago only if the post is in the
+          // same month.
+          $posted_time = $entity->getPostedTime();
+          if (date('Ymd', $posted_time) == date('Ymd')) {
+            $date = t('%ago ago', array('%ago' => $this->dateFormatter->formatInterval(REQUEST_TIME - $posted_time)));
+          }
+          else {
+            $date = $this->dateFormatter->format($posted_time, 'medium');
+          }
+          $build[$id]['timestamp'] = array(
+            '#markup' => $date,
+            '#prefix' => '<span class="feed-item-date">',
+            '#suffix' => '</span>',
+          );
+        }
+      }
 
-    // Use a different template for the summary view mode.
-    if ($view_mode == 'summary') {
-      $defaults['#theme'] = 'aggregator_summary_item';
+      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 d492904..0000000
--- 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 0000000..4f8d400
--- /dev/null
+++ b/core/modules/aggregator/templates/aggregator-feed.html.twig
@@ -0,0 +1,34 @@
+{#
+/**
+ * @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.
+ * - link: A "Read more" link on summary view mode, a feed icon on full mode.
+ * - 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 }}>
+
+  {{ title_prefix }}
+  {% if not full %}
+    <h2{{ title_attributes }}>{{ title }}</h2>
+  {% endif %}
+  {{ title_suffix }}
+
+  {{ content }}
+
+  {{ link }}
+
+</div>
diff --git a/core/modules/aggregator/templates/aggregator-item.html.twig b/core/modules/aggregator/templates/aggregator-item.html.twig
index 8c23106..5ad6b4d 100644
--- a/core/modules/aggregator/templates/aggregator-item.html.twig
+++ b/core/modules/aggregator/templates/aggregator-item.html.twig
@@ -4,12 +4,12 @@
  * 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()
  *
@@ -17,20 +17,12 @@
  */
 #}
 <div{{ attributes }}>
+  {{ 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 3cb2279..0000000
--- 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 a00b8dc..0000000
--- 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>
