diff --git a/block_field.install b/block_field.install
new file mode 100644
index 0000000..7d83904
--- /dev/null
+++ b/block_field.install
@@ -0,0 +1,51 @@
+<?php
+/**
+ * @file
+ * Install, update and uninstall functions for the Block Field module.
+ */
+
+/**
+ * Update 'block_field' definition settings to reflect new plugin format.
+ */
+function block_field_update_8001() {
+  $entity_field_manager = \Drupal::service('entity_field.manager');
+  // Loop through all entities where 'block_fields' are defined.
+  foreach($entity_field_manager->getFieldMapByFieldType('block_field') as $entity_type_id => $fields) {
+    foreach($fields as $field) {
+      // Loop through each bundle and load definition.
+      foreach($field['bundles'] as $bundle) {
+        foreach($entity_field_manager->getFieldDefinitions($entity_type_id, $bundle) as $definition) {
+          if($definition->getType() == 'block_field') {
+            $settings = $definition->get('settings');
+            // Upgrade path for patches pre-comment 20 https://www.drupal.org/project/block_field/issues/2876110
+            if(isset($settings['filter']) && $settings['filter'] == 'categories') {
+              $settings = [
+                'selection' => 'categories',
+                'selection_settings' => [
+                  'categories' => $settings['categories']
+                ]
+              ];
+            }
+            // Update settings for existing configuration.
+            else if(isset($settings['plugin_ids'])) {
+              $settings = [
+                'selection' => 'blocks',
+                'selection_settings' => [
+                  'plugin_ids' => $settings['plugin_ids']
+                ]
+              ];
+            }
+            $definition->setSettings($settings);
+            // Unset existing indices because setSettings is additive only.
+            $settings_raw = $definition->get('settings');
+            unset($settings_raw['plugin_ids']);
+            unset($settings_raw['filter']);
+            $definition->set('settings', $settings_raw);
+            $definition->save();
+
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/block_field.services.yml b/block_field.services.yml
index f8bdc23..1e7f719 100755
--- a/block_field.services.yml
+++ b/block_field.services.yml
@@ -2,3 +2,6 @@ services:
   block_field.manager:
     class: Drupal\block_field\BlockFieldManager
     arguments: ['@plugin.manager.block', '@context.repository']
+  plugin.manager.block_field_selection:
+    class: Drupal\block_field\BlockFieldSelectionManager
+    parent: default_plugin_manager
diff --git a/config/schema/block_field.yml b/config/schema/block_field.yml
index 44f2e40..b0e3582 100644
--- a/config/schema/block_field.yml
+++ b/config/schema/block_field.yml
@@ -26,7 +26,25 @@ field.storage_settings.block_field:
 
 field.field_settings.block_field:
   type: mapping
-  label: 'Block field settings'
+  label: 'Block Field field settings'
+  mapping:
+    selection:
+      type: string
+      label: 'Selection method'
+    selection_settings:
+      type: block_field_selection.[%parent.selection]
+      label: 'Block Field selection plugin settings'
+
+block_field_selection:
+  type: mapping
+  label: 'Block field selection handler settings'
+
+block_field_selection.*:
+  type: block_field_selection
+
+block_field_selection.blocks:
+  type: block_field_selection
+  label: 'Blocks(default) selection handler settings'
   mapping:
     plugin_ids:
       type: sequence
@@ -34,6 +52,16 @@ field.field_settings.block_field:
       sequence:
         type: string
 
+block_field_selection.categories:
+  type: block_field_selection
+  label: 'Categories selection handler settings'
+  mapping:
+    categories:
+      type: sequence
+      label: 'Categories'
+      sequence:
+        type: string
+
 field.value.block_field:
   type: mapping
   label: 'Default value'
diff --git a/src/Annotation/BlockFieldSelection.php b/src/Annotation/BlockFieldSelection.php
new file mode 100644
index 0000000..b20c30b
--- /dev/null
+++ b/src/Annotation/BlockFieldSelection.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\block_field\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a Block field selection item annotation object.
+ *
+ * @see \Drupal\block_field\BlockFieldSelectionManager
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class BlockFieldSelection extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The label of the plugin.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $label;
+
+}
diff --git a/src/BlockFieldManager.php b/src/BlockFieldManager.php
index 87a5166..44f5937 100644
--- a/src/BlockFieldManager.php
+++ b/src/BlockFieldManager.php
@@ -45,4 +45,12 @@ class BlockFieldManager implements BlockFieldManagerInterface {
     return $this->blockManager->getSortedDefinitions($definitions);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getBlockCategories() {
+      return $this->blockManager->getCategories();
+  }
+
+
 }
diff --git a/src/BlockFieldManagerInterface.php b/src/BlockFieldManagerInterface.php
index e57580f..8d20864 100644
--- a/src/BlockFieldManagerInterface.php
+++ b/src/BlockFieldManagerInterface.php
@@ -15,4 +15,12 @@ interface BlockFieldManagerInterface {
    */
   public function getBlockDefinitions();
 
+  /**
+   * Get list of all block categories.
+   *
+   * @return string[]
+   *   A numerically indexed array of block categories.
+   */
+  public function getBlockCategories();
+
 }
diff --git a/src/BlockFieldSelectionBase.php b/src/BlockFieldSelectionBase.php
new file mode 100644
index 0000000..af5659e
--- /dev/null
+++ b/src/BlockFieldSelectionBase.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Drupal\block_field;
+
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Base class for Block field selection plugins.
+ */
+abstract class BlockFieldSelectionBase extends PluginBase implements BlockFieldSelectionInterface, ConfigurablePluginInterface {
+
+  /**
+   * Constructs a new selection object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    // Merge in defaults.
+    $this->configuration = NestedArray::mergeDeep(
+      $this->defaultConfiguration(),
+      $configuration
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    // TODO: Implement calculateDependencies() method.
+  }
+
+  /**
+   * Move settings up a level for easier processing and storage.
+   */
+  public function formProcessMergeParent($element) {
+    $parents = $element['#parents'];
+    array_pop($parents);
+    $element['#parents'] = $parents;
+    return $element;
+  }
+
+}
diff --git a/src/BlockFieldSelectionInterface.php b/src/BlockFieldSelectionInterface.php
new file mode 100644
index 0000000..00c80ff
--- /dev/null
+++ b/src/BlockFieldSelectionInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\block_field;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Plugin\PluginFormInterface;
+
+/**
+ * Defines an interface for Block field selection plugins.
+ */
+interface BlockFieldSelectionInterface extends PluginInspectionInterface, PluginFormInterface {
+
+
+  /**
+   * Returns filtered block definitions based on plugin settings.
+   *
+   * @return array
+   */
+  public function getReferenceableBlockDefinitions();
+}
diff --git a/src/BlockFieldSelectionManager.php b/src/BlockFieldSelectionManager.php
new file mode 100644
index 0000000..d4ea810
--- /dev/null
+++ b/src/BlockFieldSelectionManager.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\block_field;
+
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Provides the Block field selection plugin manager.
+ */
+class BlockFieldSelectionManager extends DefaultPluginManager {
+
+
+  /**
+   * Constructs a new BlockFieldSelectionManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/block_field/BlockFieldSelection', $namespaces, $module_handler, 'Drupal\block_field\BlockFieldSelectionInterface', 'Drupal\block_field\Annotation\BlockFieldSelection');
+
+    $this->alterInfo('block_field_block_field_selection_info');
+    $this->setCacheBackend($cache_backend, 'block_field_block_field_selection_plugins');
+  }
+
+  /**
+   * Loads all definitions and returns key => value array.
+   * @return array
+   */
+  public function getOptions() {
+    $definitions = $this->getDefinitions();
+    $options = [];
+    foreach ($definitions as $plugin_id => $definition) {
+      $options[$plugin_id] = $definition['label'];
+    }
+    return $options;
+  }
+
+  /**
+   * Returns an instance of BlockFieldSelectionInterface based on
+   * settings from FieldDefinitionInterface $field.
+   *
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field
+   *
+   * @return object
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function getSelectionHandler(FieldDefinitionInterface $field) {
+    $settings = $field->getSetting('selection_settings') ? $field->getSetting('selection_settings') : [];
+    if ($field->getSetting('selection') && $this->hasDefinition($field->getSetting('selection'))) {
+      return $this->createInstance($field->getSetting('selection'), $settings);
+    }
+  }
+
+  /**
+   * Returns an key => value array based on allowed referenceable blocks
+   * from the BlockFieldSelectionInterface instance.
+   *
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field
+   *
+   * @return array
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function getWidgetOptions(FieldDefinitionInterface $field) {
+    $handler = $this->getSelectionHandler($field);
+    $options = [];
+    foreach($handler->getReferenceableBlockDefinitions() as $plugin_id => $definition) {
+      $category = (string) $definition['category'];
+      $options[$category][$plugin_id] = $definition['admin_label'];
+    }
+    return $options;
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/BlockFieldItem.php b/src/Plugin/Field/FieldType/BlockFieldItem.php
index 80634e1..528ac73 100644
--- a/src/Plugin/Field/FieldType/BlockFieldItem.php
+++ b/src/Plugin/Field/FieldType/BlockFieldItem.php
@@ -3,9 +3,11 @@
 namespace Drupal\block_field\Plugin\Field\FieldType;
 
 use Drupal\block_field\BlockFieldItemInterface;
+use Drupal\Console\Core\Utils\NestedArray;
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\MapDataDefinition;
 
@@ -28,8 +30,9 @@ class BlockFieldItem extends FieldItemBase implements BlockFieldItemInterface {
    */
   public static function defaultFieldSettings() {
     return [
-      'plugin_ids' => [],
-    ] + parent::defaultFieldSettings();
+        'selection' => 'blocks',
+        'selection_settings' => [],
+      ] + parent::defaultFieldSettings();
   }
 
   /**
@@ -81,42 +84,51 @@ class BlockFieldItem extends FieldItemBase implements BlockFieldItemInterface {
   public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
     $field = $form_state->getFormObject()->getEntity();
 
-    /** @var \Drupal\block_field\BlockFieldManagerInterface $block_field_manager */
-    $block_field_manager = \Drupal::service('block_field.manager');
-    $definitions = $block_field_manager->getBlockDefinitions();
-    foreach ($definitions as $plugin_id => $definition) {
-      $options[$plugin_id] = [
-        ['category' => (string) $definition['category']],
-        ['label' => $definition['admin_label'] . ' (' . $plugin_id . ')'],
-        ['provider' => $definition['provider']],
-      ];
-    }
-
-    $default_value = $field->getSetting('plugin_ids') ?: array_keys($options);
+    /** @var \Drupal\block_field\BlockFieldSelectionManager $block_field_selection_manager */
+    $block_field_selection_manager = \Drupal::service('plugin.manager.block_field_selection');
+    $options = $block_field_selection_manager->getOptions();
+    $form = [
+      '#type' => 'container',
+      '#process' => [[get_class($this), 'fieldSettingsAjaxProcess']],
+      '#element_validate' => [[get_class($this), 'fieldSettingsFormValidate']],
 
-    $element = [];
-    $element['blocks'] = [
+    ];
+    $form['selection'] = [
       '#type' => 'details',
-      '#title' => $this->t('Blocks'),
-      '#description' => $this->t('Please select available blocks.'),
-      '#open' => $field->getSetting('plugin_ids') ? TRUE : FALSE,
+      '#title' => t('Available blocks'),
+      '#open' => TRUE,
+      '#tree' => TRUE,
+      '#process' => [[get_class($this), 'formProcessMergeParent']],
     ];
-    $element['blocks']['plugin_ids'] = [
-      '#type' => 'tableselect',
-      '#header' => [
-        'Category',
-        'Label/ID',
-        'Provider',
-      ],
+
+    $form['selection']['selection'] = [
+      '#type' => 'select',
+      '#title' => t('Selection method'),
       '#options' => $options,
-      '#js_select' => TRUE,
+      '#default_value' => $field->getSetting('selection'),
       '#required' => TRUE,
-      '#empty' => t('No blocks are available.'),
-      '#parents' => ['settings', 'plugin_ids'],
-      '#element_validate' => [[get_called_class(), 'validatePluginIds']],
-      '#default_value' => array_combine($default_value, $default_value),
+      '#ajax' => TRUE,
+      '#limit_validation_errors' => [],
     ];
-    return $element;
+    $form['selection']['selection_submit'] = [
+      '#type' => 'submit',
+      '#value' => t('Change selection'),
+      '#limit_validation_errors' => [],
+      '#attributes' => [
+        'class' => ['js-hide'],
+      ],
+      '#submit' => [[get_class($this), 'settingsAjaxSubmit']],
+    ];
+
+    $form['selection']['selection_settings'] = [
+      '#type' => 'container',
+      '#attributes' => ['class' => ['block_field-settings']],
+    ];
+
+    $selection = $block_field_selection_manager->getSelectionHandler($field);
+    $form['selection']['selection_settings'] += $selection->buildConfigurationForm([], $form_state);
+
+    return $form;
   }
 
   /**
@@ -181,17 +193,78 @@ class BlockFieldItem extends FieldItemBase implements BlockFieldItemInterface {
   }
 
   /**
-   * Validates plugin_ids table select element.
+   * Render API callback: Processes the field settings form and allows access to
+   * the form state.
+   *
+   * @see static::fieldSettingsForm()
    */
-  public static function validatePluginIds(array &$element, FormStateInterface $form_state, &$complete_form) {
-    $value = array_filter($element['#value']);
-    if (array_keys($element['#options']) == array_keys($value)) {
-      $form_state->setValueForElement($element, []);
+  public static function fieldSettingsAjaxProcess($form, FormStateInterface $form_state) {
+    static::fieldSettingsAjaxProcessElement($form, $form);
+    return $form;
+  }
+
+  /**
+   * Adds block_field specific properties to AJAX form elements from the
+   * field settings form.
+   *
+   * @see static::fieldSettingsAjaxProcess()
+   */
+  public static function fieldSettingsAjaxProcessElement(&$element, $main_form) {
+    if (!empty($element['#ajax'])) {
+      $element['#ajax'] = [
+        'callback' => [get_called_class(), 'settingsAjax'],
+        'wrapper' => $main_form['#id'],
+        'element' => $main_form['#array_parents'],
+      ];
     }
-    else {
-      $form_state->setValueForElement($element, $value);
+
+    foreach (Element::children($element) as $key) {
+      static::fieldSettingsAjaxProcessElement($element[$key], $main_form);
     }
+  }
+
+  /**
+   * Ajax callback for the selection settings form.
+   *
+   * @see static::fieldSettingsForm()
+   */
+  public static function settingsAjax($form, FormStateInterface $form_state) {
+    return NestedArray::getValue($form, $form_state->getTriggeringElement()['#ajax']['element']);
+  }
+
+  /**
+   * Submit selection for the non-JS case.
+   *
+   * @see static::fieldSettingsForm()
+   */
+  public static function settingsAjaxSubmit($form, FormStateInterface $form_state) {
+    $form_state->setRebuild();
+  }
+
+  /**
+   * Render API callback: Moves block_field specific Form API elements
+   * (i.e. 'selection_settings') up a level for easier processing by the
+   * validation and submission selections.
+   */
+  public static function formProcessMergeParent($element) {
+    $parents = $element['#parents'];
+    array_pop($parents);
+    $element['#parents'] = $parents;
     return $element;
   }
 
+  /**
+   * Form element validation handler; Invokes selection plugin's validation.
+   *
+   * @param array $form
+   *   The form where the settings form is being included in.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state of the (entire) configuration form.
+   */
+  public static function fieldSettingsFormValidate(array $form, FormStateInterface $form_state) {
+    $field = $form_state->getFormObject()->getEntity();
+    $handler = \Drupal::service('plugin.manager.block_field_selection')->getSelectionHandler($field);
+    $handler->validateConfigurationForm($form, $form_state);
+  }
+
 }
diff --git a/src/Plugin/Field/FieldWidget/BlockFieldWidget.php b/src/Plugin/Field/FieldWidget/BlockFieldWidget.php
index 90c5123..673c4d0 100644
--- a/src/Plugin/Field/FieldWidget/BlockFieldWidget.php
+++ b/src/Plugin/Field/FieldWidget/BlockFieldWidget.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\block_field\Plugin\Field\FieldWidget;
 
+use Drupal\block_field\BlockFieldSelectionManager;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Block\BlockManagerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormState;
@@ -33,6 +35,13 @@ class BlockFieldWidget extends WidgetBase implements ContainerFactoryPluginInter
    */
   protected $blockManager;
 
+  /**
+   * The block field selection manager.
+   *
+   * @var \Drupal\block_field\BlockFieldSelectionManager
+   */
+  protected $blockFieldSelectionManager;
+
   /**
    * Set the block manager.
    *
@@ -52,13 +61,37 @@ class BlockFieldWidget extends WidgetBase implements ContainerFactoryPluginInter
       $plugin_definition,
       $configuration['field_definition'],
       $configuration['settings'],
-      $configuration['third_party_settings']
+      $configuration['third_party_settings'],
+      $container->get('plugin.manager.block_field_selection')
     );
     $instance->setBlockManager($container->get('plugin.manager.block'));
 
     return $instance;
   }
 
+  /**
+   * Constructs a WidgetBase object.
+   *
+   * @param string $plugin_id
+   *   The plugin_id for the widget.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The definition of the field to which the widget is associated.
+   * @param array $settings
+   *   The widget settings.
+   * @param array $third_party_settings
+   *   Any third party settings.
+   * @param \Drupal\block_field\BlockFieldSelectionManager $block_field_selection_manager
+   */
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, BlockFieldSelectionManager $block_field_selection_manager) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
+    $this->fieldDefinition = $field_definition;
+    $this->settings = $settings;
+    $this->thirdPartySettings = $third_party_settings;
+    $this->blockFieldSelectionManager = $block_field_selection_manager;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -121,28 +154,15 @@ class BlockFieldWidget extends WidgetBase implements ContainerFactoryPluginInter
       $item->settings = $item->settings ?: [];
     }
 
-    $options = [];
-    /** @var \Drupal\block_field\BlockFieldManagerInterface $block_field_manager */
-    $block_field_manager = \Drupal::service('block_field.manager');
-    $definitions = $block_field_manager->getBlockDefinitions();
-    foreach ($definitions as $id => $definition) {
-      // If allowed plugin ids are set then check that this block should be
-      // included.
-      if ($plugin_ids && !isset($plugin_ids[$id])) {
-        // Remove the definition, so that we have an accurate list of allowed
-        // blocks definitions.
-        unset($definitions[$id]);
-        continue;
+    $options = $this->blockFieldSelectionManager->getWidgetOptions($this->fieldDefinition);
+    if ($item->plugin_id) {
+      // If plugin_id not in second level arrays, unset plugin_id and settings..
+      if(!in_array($item->plugin_id, array_keys(call_user_func_array('array_merge', $options)), true)) {
+        $item->plugin_id = '';
+        $item->setting = [];
       }
-      $category = (string) $definition['category'];
-      $options[$category][$id] = $definition['admin_label'];
     }
 
-    // Make sure the plugin id is allowed, if not clear all settings.
-    if ($item->plugin_id && !isset($definitions[$item->plugin_id])) {
-      $item->plugin_id = '';
-      $item->setting = [];
-    }
 
     $element['plugin_id'] = [
       '#type' => 'select',
diff --git a/src/Plugin/block_field/BlockFieldSelection/Blocks.php b/src/Plugin/block_field/BlockFieldSelection/Blocks.php
new file mode 100644
index 0000000..a3b9fdb
--- /dev/null
+++ b/src/Plugin/block_field/BlockFieldSelection/Blocks.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Drupal\block_field\Plugin\block_field\BlockFieldSelection;
+
+use Drupal\block_field\BlockFieldSelectionBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Provides a 'categories' BlockFieldSection.
+ *
+ * @BlockFieldSelection(
+ *   id = "blocks",
+ *   label = @Translation("Blocks"),
+ * )
+ */
+class Blocks extends BlockFieldSelectionBase {
+  use StringTranslationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'plugin_ids' => []
+      ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+    /** @var \Drupal\block_field\BlockFieldManagerInterface $block_field_manager */
+    $block_field_manager = \Drupal::service('block_field.manager');
+    $definitions = $block_field_manager->getBlockDefinitions();
+    foreach ($definitions as $plugin_id => $definition) {
+      $options[$plugin_id] = [
+        ['category' => (string) $definition['category']],
+        ['label' => $definition['admin_label'] . ' (' . $plugin_id . ')'],
+        ['provider' => $definition['provider']],
+      ];
+    }
+    $default_value = !empty($this->getConfiguration()['plugin_ids']) ? $this->getConfiguration()['plugin_ids'] : array_keys($options);
+    $form['blocks'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Blocks'),
+      '#description' => $this->t('Please select available blocks.'),
+      '#open' => empty($this->getConfiguration()['plugin_ids']),
+      '#process' => [[$this, 'formProcessMergeParent']],
+    ];
+    $form['blocks']['plugin_ids'] = [
+      '#type' => 'tableselect',
+      '#header' => [
+        $this->t('Category'),
+        $this->t('Label/ID'),
+        $this->t('Provider'),
+      ],
+      '#options' => $options,
+      '#js_select' => TRUE,
+      '#required' => TRUE,
+      '#empty' => $this->t('No blocks are available.'),
+      '#element_validate' => [[get_called_class(), 'validatePluginIds']],
+      '#default_value' => array_combine($default_value, $default_value),
+    ];
+    return $form;
+  }
+
+  /**
+   * Validates plugin_ids table select element.
+   */
+  public static function validatePluginIds(array &$element, FormStateInterface $form_state, &$complete_form) {
+    $value = array_filter($element['#value']);
+    if (array_keys($element['#options']) == array_keys($value)) {
+      $form_state->setValueForElement($element, []);
+    }
+    else {
+      $form_state->setValueForElement($element, $value);
+    }
+    return $element;
+  }
+  /**
+   * {@inheritdoc}
+   */
+  public function getReferenceableBlockDefinitions() {
+    $block_field_manager = \Drupal::service('block_field.manager');
+    $definitions = $block_field_manager->getBlockDefinitions();
+    $values = !empty($this->getConfiguration()['plugin_ids']) ? $this->getConfiguration()['plugin_ids'] : array_keys($definitions);
+    $values = array_combine($values, $values);
+    return array_intersect_key($definitions, $values);
+  }
+}
diff --git a/src/Plugin/block_field/BlockFieldSelection/Categories.php b/src/Plugin/block_field/BlockFieldSelection/Categories.php
new file mode 100644
index 0000000..38bde12
--- /dev/null
+++ b/src/Plugin/block_field/BlockFieldSelection/Categories.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Drupal\block_field\Plugin\block_field\BlockFieldSelection;
+
+use Drupal\block_field\BlockFieldSelectionBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Provides a 'categories' BlockFieldSection.
+ *
+ * @BlockFieldSelection(
+ *   id = "categories",
+ *   label = @Translation("Categories"),
+ * )
+ */
+class Categories extends BlockFieldSelectionBase {
+  use StringTranslationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+        'categories' => []
+      ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+    /** @var \Drupal\block_field\BlockFieldManagerInterface $block_field_manager */
+    $block_field_manager = \Drupal::service('block_field.manager');
+    $categories = $block_field_manager->getBlockCategories();
+    $options = [];
+    foreach ($categories as $category) {
+      $category = (string) $category;
+      $options[$category] = $category;
+    }
+    $form['categories'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Categories'),
+      '#description' => $this->t('Please select available categories.'),
+      '#open' => empty($this->getConfiguration()['categories']) ,
+      '#process' => [[$this, 'formProcessMergeParent']],
+    ];
+    $default_value = !empty($this->getConfiguration()['categories']) ? $this->getConfiguration()['categories'] : array_keys($options);
+    $form['categories']['categories'] = [
+      '#type' => 'checkboxes',
+      '#header' => [
+        'Category',
+      ],
+      '#options' => $options,
+      '#js_select' => TRUE,
+      '#empty' => t('No categories are available.'),
+      '#required' => TRUE,
+      '#default_value' => array_combine($default_value, $default_value),
+    ];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getReferenceableBlockDefinitions() {
+    $block_field_manager = \Drupal::service('block_field.manager');
+    $definitions = $block_field_manager->getBlockDefinitions();
+    if(!empty($this->getConfiguration()['categories'])) {
+      $categories = array_filter($this->getConfiguration()['categories']);
+      $definitions = array_filter($definitions, function ($definition, $key) use ($categories) {
+        return isset($categories[(string) $definition['category']]);
+      }, ARRAY_FILTER_USE_BOTH);
+    }
+    return $definitions;
+  }
+}
diff --git a/tests/modules/block_field_test/config/install/field.field.node.block_field_test.field_block_field_test.yml b/tests/modules/block_field_test/config/install/field.field.node.block_field_test.field_block_field_test.yml
index b5f05cf..93f50b4 100644
--- a/tests/modules/block_field_test/config/install/field.field.node.block_field_test.field_block_field_test.yml
+++ b/tests/modules/block_field_test/config/install/field.field.node.block_field_test.field_block_field_test.yml
@@ -17,5 +17,7 @@ translatable: false
 default_value: {  }
 default_value_callback: ''
 settings:
-  plugin_ids: {  }
+  selection: blocks
+  selection_settings:
+    plugin_ids: {  }
 field_type: block_field
diff --git a/tests/modules/block_field_widget_test/config/install/field.field.node.block_node.field_block.yml b/tests/modules/block_field_widget_test/config/install/field.field.node.block_node.field_block.yml
index 35e3279..9c7103b 100644
--- a/tests/modules/block_field_widget_test/config/install/field.field.node.block_node.field_block.yml
+++ b/tests/modules/block_field_widget_test/config/install/field.field.node.block_node.field_block.yml
@@ -17,5 +17,7 @@ translatable: false
 default_value: {  }
 default_value_callback: ''
 settings:
-  plugin_ids: {  }
+  selection: blocks
+  selection_settings:
+    plugin_ids: {  }
 field_type: block_field
diff --git a/tests/src/Functional/BlockFieldTest.php b/tests/src/Functional/BlockFieldTest.php
index 09677ee..71840e0 100644
--- a/tests/src/Functional/BlockFieldTest.php
+++ b/tests/src/Functional/BlockFieldTest.php
@@ -157,7 +157,7 @@ class BlockFieldTest extends BrowserTestBase {
     $assert->responseNotContains($time);
 
     $this->drupalGet('admin/structure/types/manage/block_field_test/fields/node.block_field_test.field_block_field_test');
-    $this->drupalPostForm(NULL, ['settings[plugin_ids][page_title_block]' => FALSE], 'Save settings');
+    $this->drupalPostForm(NULL, ['settings[selection_settings][plugin_ids][page_title_block]' => FALSE, ], 'Save settings');
 
     $this->drupalGet('admin/structure/types/manage/block_field_test/fields/node.block_field_test.field_block_field_test');
     $assert->statusCodeEquals(200);
