diff --git a/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml b/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
index d615b14..2e55c76 100644
--- a/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
+++ b/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
@@ -194,7 +194,8 @@ display:
           empty_zero: false
           hide_alter_empty: true
           display_as_link: false
-          plugin_id: aggregator_title_link
+          plugin_id: field
+          type: aggregator_title
           entity_type: aggregator_feed
           entity_field: title
         url:
diff --git a/core/modules/aggregator/config/schema/aggregator.schema.yml b/core/modules/aggregator/config/schema/aggregator.schema.yml
index 85710b8..eb6c006 100644
--- a/core/modules/aggregator/config/schema/aggregator.schema.yml
+++ b/core/modules/aggregator/config/schema/aggregator.schema.yml
@@ -47,3 +47,11 @@ block.settings.aggregator_feed_block:
     feed:
       type: string
       label: 'Feed'
+
+field.formatter.settings.aggregator_title:
+  type: mapping
+  label: 'Formatter settings'
+  mapping:
+    display_as_link:
+      type: boolean
+      label: 'Display as link'
diff --git a/core/modules/aggregator/config/schema/aggregator.views.schema.yml b/core/modules/aggregator/config/schema/aggregator.views.schema.yml
index 23cc642..96313ad 100644
--- a/core/modules/aggregator/config/schema/aggregator.views.schema.yml
+++ b/core/modules/aggregator/config/schema/aggregator.views.schema.yml
@@ -8,7 +8,7 @@ views.argument.aggregator_iid:
   type: views.argument.numeric
   label: 'Aggregator item ID'
 
-views.field.aggregator_title_link:
+views.field.aggregator_title:
   type: views_field
   label: 'Title link'
   mapping:
diff --git a/core/modules/aggregator/src/AggregatorFeedViewsData.php b/core/modules/aggregator/src/AggregatorFeedViewsData.php
index 70f8684..22ce4c2 100644
--- a/core/modules/aggregator/src/AggregatorFeedViewsData.php
+++ b/core/modules/aggregator/src/AggregatorFeedViewsData.php
@@ -35,7 +35,7 @@ public function getViewsData() {
     $data['aggregator_feed']['fid']['filter']['id'] = 'numeric';
 
     $data['aggregator_feed']['title']['help'] = $this->t('The title of the aggregator feed.');
-    $data['aggregator_feed']['title']['field']['id'] = 'aggregator_title_link';
+    $data['aggregator_feed']['title']['field']['id'] = 'field';
     $data['aggregator_feed']['argument']['id'] = 'string';
 
     $data['aggregator_feed']['url']['help'] = $this->t('The fully-qualified URL of the feed.');
diff --git a/core/modules/aggregator/src/AggregatorItemViewsData.php b/core/modules/aggregator/src/AggregatorItemViewsData.php
index 2ab9e21..0f1a940 100644
--- a/core/modules/aggregator/src/AggregatorItemViewsData.php
+++ b/core/modules/aggregator/src/AggregatorItemViewsData.php
@@ -28,7 +28,7 @@ public function getViewsData() {
     $data['aggregator_item']['iid']['argument']['numeric'] = TRUE;
 
     $data['aggregator_item']['title']['help'] = $this->t('The title of the aggregator item.');
-    $data['aggregator_item']['title']['field']['id'] = 'aggregator_title_link';
+    $data['aggregator_item']['title']['field']['id'] = 'field';
     $data['aggregator_item']['title']['field']['extra'] = 'link';
 
     $data['aggregator_item']['link']['help'] = $this->t('The link to the original source URL of the item.');
diff --git a/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorTitleFormatter.php b/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorTitleFormatter.php
new file mode 100644
index 0000000..b6ed568
--- /dev/null
+++ b/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorTitleFormatter.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\Field\FieldFormatter\AggregatorTitleFormatter.
+ */
+
+namespace Drupal\aggregator\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Plugin implementation of the 'aggregator_title' formatter.
+ *
+ * @FieldFormatter(
+ *   id = "aggregator_title",
+ *   label = @Translation("Aggregator item title"),
+ *   description = @Translation("Formats the aggregator item title with an optional link."),
+ *   field_types = {
+ *     "string"
+ *   }
+ * )
+ */
+class AggregatorTitleFormatter extends FormatterBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    $options = parent::defaultSettings();
+
+    $options['display_as_link'] = TRUE;
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+    $form = parent::settingsForm($form, $form_state);
+
+    $form['display_as_link'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Link to URL'),
+      '#default_value' => $this->getSetting('display_as_link'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(FieldItemListInterface $items) {
+    $elements = [];
+
+    $url = Url::fromUri($items->getEntity()->getLink());
+
+    foreach ($items as $delta => $item) {
+        if ($this->getSetting('display_as_link')) {
+          $elements[$delta] = [
+            '#type' => 'link',
+            '#title' => $item->value,
+            '#url' => $url,
+          ];
+        }
+        else {
+          $elements[$delta] = ['#markup' => $item->value];
+        }
+      }
+
+    return $elements;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function isApplicable(FieldDefinitionInterface $field_definition) {
+    return $field_definition->getTargetEntityTypeId() === 'aggregator_item' && $field_definition->getName() === 'title';
+  }
+
+}
diff --git a/core/modules/aggregator/src/Plugin/views/field/TitleLink.php b/core/modules/aggregator/src/Plugin/views/field/TitleLink.php
deleted file mode 100644
index ba05199..0000000
--- a/core/modules/aggregator/src/Plugin/views/field/TitleLink.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\aggregator\Plugin\views\field\TitleLink.
- */
-
-namespace Drupal\aggregator\Plugin\views\field;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Url;
-use Drupal\views\Plugin\views\display\DisplayPluginBase;
-use Drupal\views\Plugin\views\field\FieldPluginBase;
-use Drupal\views\ResultRow;
-use Drupal\views\ViewExecutable;
-
-/**
- * Defines a field handler that turns an item's title into a clickable link.
- *
- * The link refers to the original source article.
- *
- * @ingroup views_field_handlers
- *
- * @ViewsField("aggregator_title_link")
- */
-class TitleLink extends FieldPluginBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
-    parent::init($view, $display, $options);
-
-    $this->additional_fields['link'] = 'link';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function defineOptions() {
-    $options = parent::defineOptions();
-
-    $options['display_as_link'] = array('default' => TRUE);
-
-    return $options;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
-    $form['display_as_link'] = array(
-      '#title' => $this->t('Display as link'),
-      '#type' => 'checkbox',
-      '#default_value' => !empty($this->options['display_as_link']),
-    );
-    parent::buildOptionsForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function render(ResultRow $values) {
-    $value = $this->getValue($values);
-    return $this->renderLink($this->sanitizeValue($value), $values);
-  }
-
-  /**
-   * Renders aggregator item's title as link.
-   *
-   * @param string $data
-   *   The XSS safe string for the link text.
-   * @param \Drupal\views\ResultRow $values
-   *   The values retrieved from a single row of a view's query result.
-   *
-   * @return string
-   *   Returns a string for the link text.
-   */
-  protected function renderLink($data, ResultRow $values) {
-    $link = $this->getValue($values, 'link');
-    if (!empty($this->options['display_as_link'])) {
-      $this->options['alter']['make_link'] = TRUE;
-      // Note: $link is an external URI, pointing to the feed itself.
-      $this->options['alter']['url'] = Url::fromUri($link);
-      $this->options['alter']['html'] = TRUE;
-      $this->options['alter']['absolute'] = TRUE;
-    }
-
-    return $data;
-  }
-
-}
diff --git a/core/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml b/core/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml
index 8e5673f..0a2657b 100644
--- a/core/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml
+++ b/core/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml
@@ -44,7 +44,8 @@ display:
           table: aggregator_item
           field: title
           id: title
-          plugin_id: aggregator_title_link
+          plugin_id: field
+          type: aggregator_title
           alter:
             alter_text: false
             text: ''
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_style_opml.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_style_opml.yml
index e601365..36f8e53 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_style_opml.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_style_opml.yml
@@ -84,7 +84,8 @@ display:
           empty_zero: false
           hide_alter_empty: true
           display_as_link: false
-          plugin_id: aggregator_title_link
+          plugin_id: field
+          type: aggregator_title
           entity_type: aggregator_feed
           entity_field: title
         url:
