diff --git a/core/modules/block/lib/Drupal/block/BlockListController.php b/core/modules/block/lib/Drupal/block/BlockListController.php
index b9d6218..e506155 100644
--- a/core/modules/block/lib/Drupal/block/BlockListController.php
+++ b/core/modules/block/lib/Drupal/block/BlockListController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Component\Utility\Json;
+use Drupal\Component\Utility\String;
 use Drupal\Core\Config\Entity\ConfigEntityListController;
 use Drupal\Core\Entity\EntityControllerInterface;
 use Drupal\Core\Entity\EntityInterface;
@@ -322,7 +323,7 @@ public function buildForm(array $form, array &$form_state) {
       return strnatcasecmp($a['admin_label'], $b['admin_label']);
     });
     foreach ($plugins as $plugin_id => $plugin_definition) {
-      $category = $plugin_definition['category'];
+      $category = String::checkPlain($plugin_definition['category']);
       if (!isset($form['right']['list'][$category])) {
         $form['right']['list'][$category] = array(
           '#type' => 'details',
diff --git a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php
index 3a082bb..1457e09 100644
--- a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php
+++ b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php
@@ -9,9 +9,11 @@
 namespace Drupal\block\Plugin\views\display;
 
 use Drupal\Component\Annotation\Plugin;
+use Drupal\Component\Utility\String;
 use Drupal\Core\Annotation\Translation;
 use Drupal\views\Plugin\Block\ViewsBlock;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\Views;
 
 /**
  * The plugin that handles a block.
@@ -46,6 +48,10 @@ protected function defineOptions() {
     $options = parent::defineOptions();
 
     $options['block_description'] = array('default' => '', 'translatable' => TRUE);
+    $base_tables = Views::viewsData()->fetchBaseTables();
+    $base_table = $this->view->storage->get('base_table');
+    $block_category = isset($base_tables[$base_table]['title']) ? $base_tables[$base_table]['title'] : 'Views';
+    $options['block_category'] = array('default' => $block_category, 'translatable' => TRUE);
     $options['block_caching'] = array('default' => DRUPAL_NO_CACHE);
 
     $options['allow'] = array(
@@ -108,12 +114,18 @@ public function optionsSummary(&$categories, &$options) {
     if (empty($block_description)) {
       $block_description = t('None');
     }
+    $block_category = String::checkPlain($this->getOption('block_category'));
 
     $options['block_description'] = array(
       'category' => 'block',
       'title' => t('Block name'),
       'value' => views_ui_truncate($block_description, 24),
     );
+    $options['block_category'] = array(
+      'category' => 'block',
+      'title' => t('Block category'),
+      'value' => views_ui_truncate($block_category, 24),
+    );
 
     $filtered_allow = array_filter($this->getOption('allow'));
 
@@ -173,6 +185,14 @@ public function buildOptionsForm(&$form, &$form_state) {
           '#default_value' => $this->getOption('block_description'),
         );
         break;
+      case 'block_category':
+        $form['#title'] .= t('Block category');
+        $form['block_category'] = array(
+          '#type' => 'textfield',
+          '#description' => t('The category this block will appear under on the <a href="@href">blocks placement page</a>.', array('@href' => '/admin/structure/blocks')),
+          '#default_value' => String::checkPlain($this->getOption('block_category')),
+        );
+        break;
       case 'block_caching':
         $form['#title'] .= t('Block caching type');
 
@@ -217,6 +237,7 @@ public function submitOptionsForm(&$form, &$form_state) {
     parent::submitOptionsForm($form, $form_state);
     switch ($form_state['section']) {
       case 'block_description':
+      case 'block_category':
       case 'block_caching':
       case 'allow':
         $this->setOption($form_state['section'], $form_state['values'][$form_state['section']]);
diff --git a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php
index 8da0d69..c3c625b 100644
--- a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php
@@ -47,6 +47,43 @@ protected function setUp() {
   }
 
   /**
+   * Tests default and custom block categories.
+   */
+  public function testBlockCategory() {
+    $this->drupalLogin($this->drupalCreateUser(array('administer views', 'access contextual links', 'administer blocks')));
+
+    // Test that the block was given a default category corresponding to its
+    // base table.
+    $arguments = array(
+      ':id' => 'edit-views-test-data',
+      ':li_class' => 'views-blocktest-view-block-block-1',
+      ':href' => 'admin/structure/block/add/views_block%3Atest_view_block-block_1/stark',
+      ':text' => 'View: test_view_block',
+    );
+    $this->drupalGet('admin/structure/block');
+    $elements = $this->xpath('//details[@id=:id]//ul[contains(@class, "block-list")]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+    $this->assertTrue(!empty($elements), 'The test block appears in the category for its base table.');
+
+    // Change the block category to a random string.
+    $this->drupalGet('admin/structure/views/view/test_view_block/edit/block_1');
+    $label = t('Views test data');
+    $link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:label]', array(':label' => $label));
+    $this->assertTrue(!empty($link));
+    $this->clickLink($label);
+    $edit = array(
+      'block_category' => $this->randomString(),
+    );
+    $this->drupalPost(NULL, $edit, t('Apply'));
+    $this->drupalPost(NULL, array(), t('Save'));
+
+    // Test that the block is listed under the custom category.
+    $arguments[':id'] = 'edit-' . drupal_html_class($edit['block_category']);
+    $this->drupalGet('admin/structure/block');
+    $elements = $this->xpath('//details[@id=:id]//ul[contains(@class, "block-list")]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+    $this->assertTrue(!empty($elements), 'The test block appears in the custom category.');
+  }
+
+  /**
    * Tests removing a block display.
    */
   protected function testDeleteBlockDisplay() {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php
index 5ac2179..6279c30 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php
@@ -100,6 +100,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
             }
           }
           $this->derivatives[$delta] = array(
+            'category' => $display->getOption('block_category'),
             'admin_label' => $desc,
             'cache' => $display->getCacheType()
           );
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php
index df65cc5..68bf105 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php
@@ -105,8 +105,8 @@ public static function create(ContainerInterface $container, array $configuratio
    *   The options configured for this plugin.
    */
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
-    $this->setOptionDefaults($this->options, $this->defineOptions());
     $this->view = $view;
+    $this->setOptionDefaults($this->options, $this->defineOptions());
     $this->displayHandler = $display;
 
     $this->unpackOptions($this->options, $options);
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 388d599..7139fa0 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -115,8 +115,8 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi
   }
 
   public function initDisplay(ViewExecutable $view, array &$display, array &$options = NULL) {
-    $this->setOptionDefaults($this->options, $this->defineOptions());
     $this->view = $view;
+    $this->setOptionDefaults($this->options, $this->defineOptions());
     $this->display = &$display;
 
     // Load extenders as soon as possible.
