diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 047026a..ceecd3f 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -347,95 +347,6 @@ function aggregator_queue_info() {
 }
 
 /**
- * Implements hook_block_info().
- */
-function aggregator_block_info() {
-  $blocks = array();
-  $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
-  foreach ($result as $category) {
-    $blocks['category-' . $category->cid]['info'] = t('!title category latest items', array('!title' => $category->title));
-  }
-  $result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
-  foreach ($result as $feed) {
-    $blocks['feed-' . $feed->fid]['info'] = t('!title feed latest items', array('!title' => $feed->title));
-  }
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function aggregator_block_configure($delta = '') {
-  list($type, $id) = explode('-', $delta);
-  if ($type == 'category') {
-    $value = db_query('SELECT block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchField();
-    $form['block'] = array(
-      '#type' => 'select',
-      '#title' => t('Number of news items in block'),
-      '#default_value' => $value,
-      '#options' => drupal_map_assoc(range(2, 20)),
-    );
-    return $form;
-  }
-}
-
-/**
- * Implements hook_block_save().
- */
-function aggregator_block_save($delta = '', $edit = array()) {
-  list($type, $id) = explode('-', $delta);
-  if ($type == 'category') {
-    db_update('aggregator_category')
-      ->fields(array('block' => $edit['block']))
-      ->condition('cid', $id)
-      ->execute();
-  }
-}
-
-/**
- * Implements hook_block_view().
- *
- * Generates blocks for the latest news items in each category and feed.
- */
-function aggregator_block_view($delta = '') {
-  if (user_access('access news feeds')) {
-    $block = array();
-    list($type, $id) = explode('-', $delta);
-    $result = FALSE;
-    switch ($type) {
-      case 'feed':
-        if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
-          $block['subject'] = check_plain($feed->title);
-          $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $id));
-          $read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
-        }
-        break;
-
-      case 'category':
-        if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
-          $block['subject'] = check_plain($category->title);
-          $result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $category->block, array(':cid' => $category->cid));
-          $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
-        }
-        break;
-    }
-
-    $items = array();
-    if (!empty($result)) {
-      foreach ($result as $item) {
-        $items[] = theme('aggregator_block_item', array('item' => $item));
-      }
-    }
-
-    // Only display the block if there are items to show.
-    if (count($items) > 0) {
-      $block['content'] = theme('item_list', array('items' => $items)) . $read_more;
-    }
-    return $block;
-  }
-}
-
-/**
  * Adds/edits/deletes aggregator categories.
  *
  * @param $edit
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/CategoryBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/CategoryBlock.php
new file mode 100644
index 0000000..1bd3af8
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/CategoryBlock.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+class CategoryBlock implements DerivativeInterface {
+  protected $derivatives = array();
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title WHERE cid = :cid', array(':cid' => $derivative_id))->fetchObject();
+    $this->derivatives[$derivative_id] = $base_plugin_definition;
+    $this->derivatives[$derivative_id]['delta'] = $result->cid;
+    $this->derivatives[$derivative_id]['subject'] = t('@title category latest items', array('@title' => $result->title));
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
+    foreach ($result as $category) {
+      $this->derivatives[$category->cid] = $this->config;
+      $this->derivatives[$category->cid]['delta'] = $category->cid;
+      $this->derivatives[$category->cid]['subject'] = t('@title category latest items', array('@title' => $category->title));
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/FeedBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/FeedBlock.php
new file mode 100644
index 0000000..3daf44e
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/FeedBlock.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+class FeedBlock implements DerivativeInterface {
+  protected $derivatives = array();
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title WHERE cid = :cid', array(':cid' => $derivative_id))->fetchObject();
+    $this->derivatives[$derivative_id] = $base_plugin_definition;
+    $this->derivatives[$derivative_id]['delta'] = $result->cid;
+    $this->derivatives[$derivative_id]['subject'] = t('@title feed latest items', array('@title' => $result->title));
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    $result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
+    foreach ($result as $feed) {
+      $this->derivatives[$feed->fid] = $this->config;
+      $this->derivatives[$feed->fid]['delta'] = $feed->fid;
+      $this->derivatives[$feed->fid]['subject'] = t('@title feed latest items', array('@title' => $feed->title));
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/CategoryBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/CategoryBlock.php
new file mode 100644
index 0000000..e65ad2a
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/CategoryBlock.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "aggregator_category_block",
+ *   subject = @Translation("Aggregator category"),
+ *   module = "aggregator",
+ *   derivative = "Drupal\aggregator\Plugin\Derivative\CategoryBlock"
+ * )
+ */
+class CategoryBlock extends BlockBase {
+
+  /**
+   * Implements BlockInterface::settings().
+   */
+  public function settings() {
+    return array(
+      'block_count' => 10,
+    );
+  }
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return user_access('access news feeds');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of news items in block'),
+      '#default_value' => $this->config['settings']['block_count'],
+      '#options' => drupal_map_assoc(range(2, 20)),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    $this->config['settings']['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $id = $this->getPluginId();
+    if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
+      $result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $this->config['settings']['block_count'], array(':cid' => $category->cid));
+      $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
+
+      $items = array();
+      foreach ($result as $item) {
+        $items[] = theme('aggregator_block_item', array('item' => $item));
+      }
+
+      // Only display the block if there are items to show.
+      if (count($items) > 0) {
+        return array(
+          '#children' => theme('item_list', array('items' => $items)) . $read_more,
+        );
+      }
+      return array();
+    }
+  }
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/FeedBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/FeedBlock.php
new file mode 100644
index 0000000..db418ec
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/FeedBlock.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "aggregator_feed_block",
+ *   subject = @Translation("Aggregator feed"),
+ *   module = "aggregator",
+ *   derivative = "Drupal\aggregator\Plugin\Derivative\FeedBlock"
+ * )
+ */
+class FeedBlock extends BlockBase {
+
+  /**
+   * Implements BlockInterface::settings().
+   */
+  public function settings() {
+    return array(
+      'block_count' => 10,
+    );
+  }
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return user_access('access news feeds');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of news items in block'),
+      '#default_value' => $this->config['block_count'],
+      '#options' => drupal_map_assoc(range(2, 20)),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    $this->config['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $id = $this->getPluginId();
+    if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
+      $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $this->config['block_count'], array(':fid' => $id));
+      $read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
+
+      $items = array();
+      foreach ($result as $item) {
+        $items[] = theme('aggregator_block_item', array('item' => $item));
+      }
+
+      // Only display the block if there are items to show.
+      if (count($items) > 0) {
+        return array(
+          '#children' => theme('item_list', array('items' => $items)) . $read_more,
+        );
+      }
+    }
+  }
+}
