diff --git a/core/modules/block/custom_block/custom_block.local_actions.yml b/core/modules/block/custom_block/custom_block.local_actions.yml
new file mode 100644
index 0000000..b272691
--- /dev/null
+++ b/core/modules/block/custom_block/custom_block.local_actions.yml
@@ -0,0 +1,5 @@
+custom_block_type_add:
+  route_name: custom_block_type_add
+  title: 'Add custom block type'
+  appears_on:
+    - custom_block_type_list
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
index a9f6ead..9c892de 100644
--- a/core/modules/block/custom_block/custom_block.module
+++ b/core/modules/block/custom_block/custom_block.module
@@ -9,6 +9,21 @@
 use Drupal\custom_block\Plugin\Core\Entity\CustomBlock;
 
 /**
+ * Implements hook_help().
+ */
+function custom_block_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#custom_block':
+      return t('Allows the creation of custom blocks through the user interface.');
+
+    case 'admin/structure/custom-blocks/types':
+      $output = '<p>' . t('This page lists user-created blocks. These blocks are derived from block types. A block type can consist of different fields and display settings. From the block types tab you can manage these fields as well as create new block types.') . '</p>';
+      return $output;
+
+  }
+}
+
+/**
  * Implements hook_menu_local_tasks().
  */
 function custom_block_menu_local_tasks(&$data, $router_item, $root_path) {
@@ -25,6 +40,19 @@ function custom_block_menu_local_tasks(&$data, $router_item, $root_path) {
       );
     }
   }
+  if ($router_item['route_name'] == 'custom_block_list') {
+    // @todo Move to a LocalAction plugin when https://drupal.org/node/2045267
+    //   allows local actions to work with query strings.
+    $item = menu_get_item('block/add');
+    if ($item['access']) {
+      // Add a destination parameter.
+      $item['localized_options']['query']['destination'] = 'admin/structure/custom-blocks';
+      $data['actions']['block/add'] = array(
+        '#theme' => 'menu_local_action',
+        '#link' => $item,
+      );
+    }
+  }
 }
 
 /**
@@ -32,11 +60,20 @@ function custom_block_menu_local_tasks(&$data, $router_item, $root_path) {
  */
 function custom_block_menu() {
   $items['admin/structure/custom-blocks'] = array(
-    'title' => 'Custom block types',
-    'description' => 'Manage custom block types.',
+    'title' => 'Custom blocks',
+    'description' => 'Manage custom blocks.',
+    'route_name' => 'custom_block_list',
+  );
+  $items['admin/structure/custom-blocks/list'] = array(
+    'title' => 'Blocks',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['admin/structure/custom-blocks/types'] = array(
+    'title' => 'Types',
     'route_name' => 'custom_block_type_list',
+    'type' => MENU_LOCAL_TASK,
   );
-  $items['admin/structure/custom-blocks/add'] = array(
+  $items['admin/structure/custom-blocks/types/add'] = array(
     'route_name' => 'custom_block_type_add',
     'type' => MENU_SIBLING_LOCAL_TASK,
     'weight' => 1,
@@ -93,21 +130,6 @@ function custom_block_menu() {
 }
 
 /**
- * Implements hook_local_actions().
- */
-function custom_block_local_actions() {
-  return array(
-    array(
-      'route_name' => 'custom_block_type_add',
-      'title' => t('Add custom block type'),
-      'appears_on' => array(
-        'custom_block_type_list',
-      ),
-    ),
-  );
-}
-
-/**
  * Implements hook_theme().
  */
 function custom_block_theme($existing, $type, $theme, $path) {
diff --git a/core/modules/block/custom_block/custom_block.pages.inc b/core/modules/block/custom_block/custom_block.pages.inc
index ae7de30..68f3b78 100644
--- a/core/modules/block/custom_block/custom_block.pages.inc
+++ b/core/modules/block/custom_block/custom_block.pages.inc
@@ -73,5 +73,5 @@ function custom_block_delete_form_submit($form, &$form_state) {
   drupal_set_message(t('Custom block %label has been deleted.', array('%label' => $block->label())));
   watchdog('custom_block', 'Custom block %label has been deleted.', array('%label' => $block->label()), WATCHDOG_NOTICE);
 
-  $form_state['redirect'] = 'admin/structure/block';
+  $form_state['redirect'] = 'admin/structure/custom-blocks';
 }
diff --git a/core/modules/block/custom_block/custom_block.routing.yml b/core/modules/block/custom_block/custom_block.routing.yml
index 02bad13..52e1575 100644
--- a/core/modules/block/custom_block/custom_block.routing.yml
+++ b/core/modules/block/custom_block/custom_block.routing.yml
@@ -1,5 +1,5 @@
 custom_block_type_list:
-  pattern: '/admin/structure/custom-blocks'
+  pattern: '/admin/structure/custom-blocks/types'
   defaults:
     _entity_list: 'custom_block_type'
   requirements:
@@ -35,7 +35,7 @@ custom_block_edit:
     custom_block: \d+
 
 custom_block_type_add:
-  pattern: '/admin/structure/custom-blocks/add'
+  pattern: '/admin/structure/custom-blocks/types/add'
   defaults:
     _entity_form: 'custom_block_type.add'
   requirements:
@@ -47,3 +47,10 @@ custom_block_type_edit:
     _entity_form: 'custom_block_type.edit'
   requirements:
     _entity_access: 'custom_block_type.update'
+
+custom_block_list:
+  pattern: '/admin/structure/custom-blocks'
+  defaults:
+    _entity_list: 'custom_block'
+  requirements:
+    _permission: 'administer blocks'
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php
index cde78ae..20d748d 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php
@@ -191,7 +191,7 @@ public function save(array $form, array &$form_state) {
         }
       }
       else {
-        $form_state['redirect'] = 'admin/structure/block';
+        $form_state['redirect'] = 'admin/structure/custom-blocks';
       }
     }
     else {
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockListController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockListController.php
new file mode 100644
index 0000000..217e7ae
--- /dev/null
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockListController.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\custom_block\CustomBlockListController.
+ */
+
+namespace Drupal\custom_block;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityListController;
+
+/**
+ * Provides a listing of custom block entities.
+ */
+class CustomBlockListController extends EntityListController {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header = parent::buildHeader();
+    unset($header['id']);
+    return $header;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row = parent::buildRow($entity);
+    unset($row['id']);
+    return $row;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations(EntityInterface $entity) {
+    $operations = parent::getOperations($entity);
+    // The custom block edit path does not contain '/edit'.
+    if (isset($operations['edit'])) {
+      $uri = $entity->uri();
+      $operations['edit']['href'] = $uri['path'];
+      $operations['edit']['query']['destination'] = 'admin/structure/custom-blocks';
+    }
+    return $operations;
+  }
+
+}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php
index a98730e..06e92a5 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php
@@ -101,7 +101,7 @@ public function save(array $form, array &$form_state) {
       watchdog('custom_block', 'Custom block type %label has been added.', array('%label' => $block_type->label()), WATCHDOG_NOTICE, l(t('Edit'), $uri['path'] . '/edit'));
     }
 
-    $form_state['redirect'] = 'admin/structure/custom-blocks';
+    $form_state['redirect'] = 'admin/structure/custom-blocks/types';
   }
 
   /**
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeListController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeListController.php
index aaf2676..ed44d75 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeListController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeListController.php
@@ -66,4 +66,13 @@ public function buildRow(EntityInterface $entity) {
     return $row;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    // @todo Remove this once https://drupal.org/node/2032535 is in.
+    drupal_set_title(t('Custom block types'));
+    return parent::render();
+  }
+
 }
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Form/CustomBlockTypeDeleteForm.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Form/CustomBlockTypeDeleteForm.php
index 7e595d2..4b7c220 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Form/CustomBlockTypeDeleteForm.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Form/CustomBlockTypeDeleteForm.php
@@ -60,7 +60,7 @@ public function getQuestion() {
    * {@inheritdoc}
    */
   public function getCancelPath() {
-    return 'admin/structure/custom-blocks';
+    return 'admin/structure/custom-blocks/types';
   }
 
   /**
@@ -90,7 +90,7 @@ public function buildForm(array $form, array &$form_state, Request $request = NU
    */
   public function submit(array $form, array &$form_state) {
     $this->entity->delete();
-    $form_state['redirect'] = 'admin/structure/custom-blocks';
+    $form_state['redirect'] = 'admin/structure/custom-blocks/types';
     drupal_set_message(t('Custom block type %label has been deleted.', array('%label' => $this->entity->label())));
     watchdog('custom_block', 'Custom block type %label has been deleted.', array('%label' => $this->entity->label()), WATCHDOG_NOTICE);
   }
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
index 6bc0f4d..97722f6 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
@@ -24,6 +24,7 @@
  *   controllers = {
  *     "storage" = "Drupal\custom_block\CustomBlockStorageController",
  *     "access" = "Drupal\custom_block\CustomBlockAccessController",
+ *     "list" = "Drupal\custom_block\CustomBlockListController",
  *     "render" = "Drupal\custom_block\CustomBlockRenderController",
  *     "form" = {
  *       "add" = "Drupal\custom_block\CustomBlockFormController",
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php
index 4fe8347..3ce4513 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php
@@ -157,7 +157,9 @@ public function testBlockDelete() {
       'settings[label]' => $edit['info'],
       'region' => 'sidebar_first',
     );
-    $this->drupalPost(NULL, $instance, t('Save block'));
+    $block = entity_load('custom_block', 1);
+    $url = 'admin/structure/block/add/custom_block:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
+    $this->drupalPost($url, $instance, t('Save block'));
 
     $block = custom_block_load(1);
 
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
index da5ac4f..7c1d7d5 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
@@ -99,13 +99,15 @@ public function testBlockFields() {
       $this->field['field_name'] . '[und][0][title]' => 'Example.com'
     );
     $this->drupalPost(NULL, $edit, t('Save'));
+    $block = entity_load('custom_block', 1);
+    $url = 'admin/structure/block/add/custom_block:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
     // Place the block.
     $instance = array(
       'machine_name' => drupal_strtolower($edit['info']),
       'settings[label]' => $edit['info'],
       'region' => 'sidebar_first',
     );
-    $this->drupalPost(NULL, $instance, t('Save block'));
+    $this->drupalPost($url, $instance, t('Save block'));
     // Navigate to home page.
     $this->drupalGet('<front>');
     $this->assertLinkByHref('http://example.com');
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockListTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockListTest.php
new file mode 100644
index 0000000..f9b43c1
--- /dev/null
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockListTest.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\custom_block\Tests\CustomBlockListTest.
+ */
+
+namespace Drupal\custom_block\Tests;
+
+use Drupal\Core\Language\Language;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests the listing of custom blocks.
+ *
+ * @see \Drupal\block\CustomBlockListController
+ */
+class CustomBlockListTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block', 'custom_block');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block listing',
+      'description' => 'Tests the listing of custom blocks.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Tests the custom block listing page.
+   */
+  public function testListing() {
+    $this->drupalLogin($this->drupalCreateUser(array('administer blocks')));
+    $this->drupalGet('admin/structure/custom-blocks');
+
+    // Test for the page title.
+    $this->assertTitle(t('Custom blocks') . ' | Drupal');
+
+    // Test for the table.
+    $element = $this->xpath('//div[@class="l-content"]//table');
+    $this->assertTrue($element, 'Configuration entity list table found.');
+
+    // Test the table header.
+    $elements = $this->xpath('//div[@class="l-content"]//table/thead/tr/th');
+    $this->assertEqual(count($elements), 2, 'Correct number of table header cells found.');
+
+    // Test the contents of each th cell.
+    $expected_items = array(t('Label'), t('Operations'));
+    foreach ($elements as $key => $element) {
+      $this->assertIdentical((string) $element[0], $expected_items[$key]);
+    }
+
+    $label = 'Antelope';
+    $new_label = 'Albatross';
+    // Add a new entity using the operations link.
+    $link_text = t('Add custom block');
+    $this->assertLink($link_text);
+    $this->clickLink($link_text);
+    $this->assertResponse(200);
+    $edit = array();
+    $langcode = Language::LANGCODE_NOT_SPECIFIED;
+    $edit['info'] = $label;
+    $edit["block_body[$langcode][0][value]"] = $this->randomName(16);
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Confirm that once the user returns to the listing, the text of the label
+    // (versus elsewhere on the page).
+    $this->assertFieldByXpath('//td', $label, 'Label found for added block.');
+
+    // Check the number of table row cells.
+    $elements = $this->xpath('//div[@class="l-content"]//table/tbody/tr[@class="odd"]/td');
+    $this->assertEqual(count($elements), 2, 'Correct number of table row cells found.');
+    // Check the contents of each row cell. The first cell contains the label,
+    // the second contains the machine name, and the third contains the
+    // operations list.
+    $this->assertIdentical((string) $elements[0], $label);
+
+    // Edit the entity using the operations link.
+    $blocks = $this->container
+      ->get('plugin.manager.entity')
+      ->getStorageController('custom_block')
+      ->loadByProperties(array('info' => $label));
+    $block = reset($blocks);
+    if (!empty($block)) {
+      $this->assertLinkByHref('block/' . $block->id());
+      $this->clickLink(t('Edit'));
+      $this->assertResponse(200);
+      $this->assertTitle(strip_tags(t('Edit custom block %label', array('%label' => $label)) . ' | Drupal'));
+      $edit = array('info' => $new_label);
+      $this->drupalPost(NULL, $edit, t('Save'));
+    }
+    else {
+      $this->fail('Did not find Albatross block in the database.');
+    }
+
+    // Confirm that once the user returns to the listing, the text of the label
+    // (versus elsewhere on the page).
+    $this->assertFieldByXpath('//td', $new_label, 'Label found for updated custom block.');
+
+    // Delete the added entity using the operations link.
+    $this->assertLinkByHref('block/' . $block->id() . '/delete');
+    $delete_text = t('Delete');
+    $this->clickLink($delete_text);
+    $this->assertResponse(200);
+    $this->assertTitle(strip_tags(t('Are you sure you want to delete %label?', array('%label' => $new_label)) . ' | Drupal'));
+    $this->drupalPost(NULL, array(), $delete_text);
+
+    // Verify that the text of the label and machine name does not appear in
+    // the list (though it may appear elsewhere on the page).
+    $this->assertNoFieldByXpath('//td', $new_label, 'No label found for deleted custom block.');
+
+    // Confirm that the empty text is displayed.
+    $this->assertText(t('There is no Custom Block yet.'));
+  }
+
+}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTypeTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTypeTest.php
index 37dbf79..4e8c7d6 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTypeTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTypeTest.php
@@ -61,7 +61,7 @@ public function testCustomBlockTypeCreation() {
       'id' => 'foo',
       'label' => 'title for foo',
     );
-    $this->drupalPost('admin/structure/custom-blocks/add', $edit, t('Save'));
+    $this->drupalPost('admin/structure/custom-blocks/types/add', $edit, t('Save'));
     $block_type = entity_load('custom_block_type', 'foo');
     $this->assertTrue($block_type, 'The new block type has been created.');
 
