diff --git a/config/schema/config_selector.schema.yml b/config/schema/config_selector.schema.yml
index 57147ed..014b51c 100644
--- a/config/schema/config_selector.schema.yml
+++ b/config/schema/config_selector.schema.yml
@@ -1,3 +1,17 @@
+config_selector.feature.*:
+  type: config_entity
+  label: 'Configuration Selector Feature'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+    description:
+      type: label
+      label: 'Description'
+
 config_selector_third_party:
   type: mapping
   label: 'Schema for Configuration Selector additions to configuration entities'
diff --git a/config_selector.routing.yml b/config_selector.routing.yml
new file mode 100644
index 0000000..f37ee3e
--- /dev/null
+++ b/config_selector.routing.yml
@@ -0,0 +1,48 @@
+entity.config_selector_feature.delete_form:
+  path: '/admin/structure/config_selector/manage/{config_selector_feature}/delete'
+  defaults:
+    _entity_form: 'config_selector_feature.delete'
+    _title: 'Delete Configuration Selector feature'
+  requirements:
+    _entity_access: 'config_selector_feature.delete'
+
+entity.config_selector_feature.collection:
+  path: '/admin/structure/config_selector'
+  defaults:
+    _entity_list: 'config_selector_feature'
+    _title: 'Features'
+  requirements:
+    _permission: 'administer site configuration'
+
+config_select.feature.add:
+  path: '/admin/structure/config_selector/add-feature'
+  defaults:
+    _entity_form: 'config_selector_feature.add'
+    _title: 'Add Configuration Selector feature'
+  requirements:
+    _entity_create_access: 'config_selector_feature'
+
+entity.config_selector_feature.edit_form:
+  path: '/admin/structure/config_selector/manage/{config_selector_feature}/edit'
+  defaults:
+    _entity_form: 'config_selector_feature.edit'
+    _title: 'Edit Configuration Selector feature'
+  requirements:
+    _entity_access: 'config_selector_feature.update'
+
+entity.config_selector_feature.manage:
+  path: '/admin/structure/config_selector/manage/{config_selector_feature}'
+  defaults:
+    _entity_form: 'config_selector_feature.default'
+    _title: 'Manage Configuration Selector feature'
+  requirements:
+    _entity_access: 'config_selector_feature.manage'
+
+config_selector.select:
+  path: '/admin/structure/config_selector/switch/{config_selector_feature}/{config_entity_type}/{config_entity_id}/select}'
+  defaults:
+    _controller: '\Drupal\config_selector\Controller\Switcher::select'
+    entity_type: 'config_selector_feature'
+  requirements:
+    _permission: 'administer site configuration'
+    _csrf_token: 'TRUE'
diff --git a/src/Controller/Switcher.php b/src/Controller/Switcher.php
new file mode 100644
index 0000000..2ecddff
--- /dev/null
+++ b/src/Controller/Switcher.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\config_selector\Controller;
+
+use Drupal\config_selector\ConfigSelectorSortTrait;
+use Drupal\config_selector\Entity\FeatureInterface;
+use Drupal\Core\Controller\ControllerBase;
+
+/**
+ * Allows the UI to switch between different configuration entities.
+ */
+class Switcher extends ControllerBase {
+  use ConfigSelectorSortTrait;
+
+  /**
+   * Selects the supplied configuration entity.
+   *
+   * @param \Drupal\config_selector\Entity\FeatureInterface $config_selector_feature
+   *   The Configuration selector feature.
+   * @param string $config_entity_type
+   *   The entity type of the configuration entity we are switching to.
+   * @param string $config_entity_id
+   *   The ID of the configuration entity we are switching to.
+   *
+   * @return \Symfony\Component\HttpFoundation\RedirectResponse
+   *   This always redirects to the feature's manage route.
+   */
+  public function select(FeatureInterface $config_selector_feature, $config_entity_type, $config_entity_id) {
+    $redirect = $this->redirect('entity.config_selector_feature.manage', ['config_selector_feature' => $config_selector_feature->id()]);
+    /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $config_entity */
+    $config_entity = $this->getConfigEntity($config_selector_feature, $config_entity_type, $config_entity_id);
+    if (!$config_entity) {
+      return $redirect;
+    }
+
+    // Enable the entity and disable the others.
+    $config_entity->setStatus(TRUE);
+    $config_entity->save();
+
+    $entities = $config_selector_feature->getConfigurationByType($config_entity_type);
+    unset($entities[$config_entity->id()]);
+    foreach ($entities as $entity) {
+      if ($entity->status()) {
+        $entity->setStatus(FALSE);
+        $entity->save();
+      }
+    }
+
+    $this->messenger()->addStatus($this->t('Configuration entity %config_label has been selected.', ['%config_label' => $config_entity->label()]));
+    return $redirect;
+  }
+
+  /**
+   * Gets a valid configuration entity to work with.
+   *
+   * @param \Drupal\config_selector\Entity\FeatureInterface $config_selector_feature
+   *   The Configuration selector feature.
+   * @param string $config_entity_type
+   *   The entity type of the configuration entity we are switching to.
+   * @param string $config_entity_id
+   *   The ID of the configuration entity we are switching to.
+   *
+   * @return \Drupal\Core\Config\Entity\ConfigEntityInterface|false
+   *   The configuration entity we are switching to, or FALSE if invalid.
+   */
+  protected function getConfigEntity(FeatureInterface $config_selector_feature, $config_entity_type, $config_entity_id) {
+    /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $config_entity */
+    $config_entity = $this->entityTypeManager()->getStorage($config_entity_type)->load($config_entity_id);
+    if (!$config_entity) {
+      $this->messenger()->addWarning($this->t('Configuration entity of type %type and ID $id does not exist.', ['%type' => $config_entity_type, '%id' => $config_entity_id]));
+      return FALSE;
+    }
+    if ($config_entity->getThirdPartySetting('config_selector', 'feature') !== $config_selector_feature->id()) {
+      $this->messenger()->addWarning($this->t('Configuration entity %config_label is not part of the %feature_label feature.', ['%config_label' => $config_entity->label(), '%feature_label' => $config_selector_feature->label()]));
+      return FALSE;
+    }
+    return $config_entity;
+  }
+
+}
diff --git a/src/Entity/Feature.php b/src/Entity/Feature.php
new file mode 100644
index 0000000..4cdcf6e
--- /dev/null
+++ b/src/Entity/Feature.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Drupal\config_selector\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
+
+/**
+ * Defines the Configuration Selector feature configuration entity.
+ *
+ * @todo the edit/add/delete forms are missing but the plumbing is left in to
+ *   make adding at a later date simple.
+ *
+ * @ConfigEntityType(
+ *   id = "config_selector_feature",
+ *   label = @Translation("Configuration selector feature"),
+ *   handlers = {
+ *     "access" = "Drupal\config_selector\FeatureAccessControlHandler",
+ *     "list_builder" = "Drupal\config_selector\FeatureListBuilder",
+ *     "form" = {
+ *       "default" = "Drupal\config_selector\Form\FeatureManageForm",
+ *       "add" = "Drupal\config_selector\Form\FeatureAddForm",
+ *       "edit" = "Drupal\config_selector\Form\FeatureEditForm",
+ *       "delete" = "Drupal\config_selector\Form\FeatureDeleteForm"
+ *     }
+ *   },
+ *   config_prefix = "feature",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label"
+ *   },
+ *   links = {
+ *     "manage" = "/admin/structure/config_selector/manage/{config_selector_feature}",
+ *     "delete-form" = "/admin/structure/config_selector/manage/{config_selector_feature}/delete",
+ *     "edit-form" = "/admin/structure/config_selector/manage/{config_selector_feature}/edit",
+ *     "collection" = "/admin/structure/config_selector",
+ *   },
+ *   config_export = {
+ *     "id",
+ *     "label",
+ *     "description",
+ *   }
+ * )
+ */
+class Feature extends ConfigEntityBase implements FeatureInterface {
+
+  /**
+   * The machine name for the configuration entity.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The human-readable name of the configuration entity.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * The description of the feature.
+   *
+   * @var string
+   */
+  protected $description;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->description;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    $configuration = [];
+
+    foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type) {
+      if ($entity_type instanceof ConfigEntityTypeInterface) {
+        $result = $this->getConfigurationByType($entity_type->id());
+        if (!empty($result)) {
+          $configuration[$entity_type->id()] = $result;
+        }
+      }
+    }
+    return $configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfigurationByType($entity_type_id) {
+    $entity_storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
+
+    $result = $entity_storage
+      ->getQuery()
+      ->condition('third_party_settings.config_selector.feature', $this->id())
+      ->execute();
+
+    if (!empty($result)) {
+      // Convert the result to entities.
+      $result = $entity_storage->loadMultiple($result);
+    }
+    return $result;
+  }
+
+}
diff --git a/src/Entity/FeatureInterface.php b/src/Entity/FeatureInterface.php
new file mode 100644
index 0000000..b7a6fda
--- /dev/null
+++ b/src/Entity/FeatureInterface.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\config_selector\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Provides an interface defining a Configuration Selector feature entity.
+ */
+interface FeatureInterface extends ConfigEntityInterface {
+
+  /**
+   * Gets the feature's description.
+   *
+   * @return string
+   *   The feature's description.
+   */
+  public function getDescription();
+
+  /**
+   * Gets the all feature's configuration entities.
+   *
+   * @return array
+   *   The arrays of configuration entity objects keyed by their entity type ID.
+   */
+  public function getConfiguration();
+
+  /**
+   * Gets the feature's configuration entities of the specified type.
+   *
+   * @param string $entity_type_id
+   *   The entity type of the returned configuration entities.
+   *
+   * @return \Drupal\Core\Config\Entity\ConfigEntityInterface[]
+   *   The feature's configuration entities of the specified type. Keyed by
+   *   their ID.
+   */
+  public function getConfigurationByType($entity_type_id);
+
+}
diff --git a/src/FeatureAccessControlHandler.php b/src/FeatureAccessControlHandler.php
new file mode 100644
index 0000000..b0611c2
--- /dev/null
+++ b/src/FeatureAccessControlHandler.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\config_selector;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Defines the access control handler for the feature entity type.
+ *
+ * @see \Drupal\config_selector\Entity\Feature
+ */
+class FeatureAccessControlHandler extends EntityAccessControlHandler {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+    switch ($operation) {
+      case 'view':
+      case 'manage':
+        return AccessResult::allowedIf($account->hasPermission('administer site configuration'))->cachePerPermissions();
+
+      // Leave plumbing for editing and deleting via the UI.
+      // @todo implement.
+      case 'update':
+      case 'delete':
+        return AccessResult::forbidden();
+
+      default:
+        // No opinion.
+        return AccessResult::neutral();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    // Leave plumbing for creating via the UI.
+    // @todo implement.
+    return AccessResult::forbidden();
+  }
+
+}
diff --git a/src/FeatureListBuilder.php b/src/FeatureListBuilder.php
new file mode 100644
index 0000000..d25bcdd
--- /dev/null
+++ b/src/FeatureListBuilder.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\config_selector;
+
+use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Builds a listing of Configuration Selector feature entities.
+ *
+ * @see \Drupal\config_selector\Entity\Feature
+ */
+class FeatureListBuilder extends ConfigEntityListBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header['name'] = $this->t('Name');
+    $header['description'] = $this->t('Description');
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOperations(EntityInterface $entity) {
+    $operations = parent::getDefaultOperations($entity);
+
+    $operations['manage'] = [
+      'title' => $this->t('Manage configuration'),
+      'url' => $entity->toUrl('manage'),
+    ];
+    return $operations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    /** @var \Drupal\config_selector\Entity\FeatureInterface $entity */
+    $row['name'] = $entity->label();
+    $row['description'] = $entity->getDescription();
+    return $row + parent::buildRow($entity);
+  }
+
+}
diff --git a/src/Form/FeatureManageForm.php b/src/Form/FeatureManageForm.php
new file mode 100644
index 0000000..41334dd
--- /dev/null
+++ b/src/Form/FeatureManageForm.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Drupal\config_selector\Form;
+
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Builds the feature manage form.
+ */
+class FeatureManageForm extends EntityForm {
+
+  /**
+   * The entity being used by this form.
+   *
+   * @var \Drupal\config_selector\Entity\FeatureInterface
+   */
+  protected $entity;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form = parent::form($form, $form_state);
+    $form['#title'] = $this->t('Manage @feature', ['@feature' => $this->entity->label()]);
+
+    $form['description'] = [
+      '#prefix' => '<p>',
+      '#markup' => $this->entity->getDescription(),
+      '#suffix' => '</p>',
+    ];
+
+    $form['configuration_header'] = [
+      '#prefix' => '<h3>',
+      '#markup' => $this->t('Included configuration:'),
+      '#suffix' => '</h3>',
+    ];
+
+    foreach ($this->entity->getConfiguration() as $entity_type_id => $config_entities) {
+      $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
+
+      $form['configuration'][$entity_type_id]['table'] = [
+        '#type' => 'table',
+        '#header' => [
+          $entity_type->getLabel(),
+          $this->t('Priority'),
+          $this->t('Status'),
+          $this->t('Operations'),
+        ],
+      ];
+      /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $config_entity */
+      foreach ($config_entities as $id => $config_entity) {
+        $form['configuration'][$entity_type_id]['table'][$id]['name'] = [
+          '#markup' => $config_entity->label(),
+        ];
+        $form['configuration'][$entity_type_id]['table'][$id]['priority'] = [
+          '#markup' => $config_entity->getThirdPartySetting('config_selector', 'priority', 0),
+        ];
+        $form['configuration'][$entity_type_id]['table'][$id]['status'] = [
+          '#markup' => $config_entity->status() ? $this->t('Enabled') : $this->t('Disabled'),
+        ];
+
+        $links = [];
+        if (!$config_entity->status()) {
+          $links['enable'] = [
+            'title' => $this->t('Select'),
+            'url' => Url::fromRoute('config_selector.select', [
+              'config_selector_feature' => $this->entity->id(),
+              'config_entity_type' => $entity_type_id,
+              'config_entity_id' => $config_entity->id(),
+            ]),
+          ];
+        }
+        $links['edit'] = [
+          'title' => $this->t('Edit configuration'),
+          'url' => $config_entity->toUrl('edit-form'),
+        ];
+        $form['configuration'][$entity_type_id]['table'][$id]['operations'] = [
+          '#type' => 'operations',
+          '#links' => $links,
+        ];
+      }
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function actions(array $form, FormStateInterface $form_state) {
+    // There are no actions.
+    return [];
+  }
+
+}
diff --git a/tests/modules/config_selector_ui_test/config/install/config_selector.feature.feature_test.yml b/tests/modules/config_selector_ui_test/config/install/config_selector.feature.feature_test.yml
new file mode 100644
index 0000000..3fdc4fd
--- /dev/null
+++ b/tests/modules/config_selector_ui_test/config/install/config_selector.feature.feature_test.yml
@@ -0,0 +1,9 @@
+langcode: en
+status: true
+dependencies:
+  enforced:
+    module:
+      - config_selector_ui_test
+id: feature_test
+label: 'Feature test'
+description: 'The main view for editing content'
diff --git a/tests/modules/config_selector_ui_test/config_selector_ui_test.info.yml b/tests/modules/config_selector_ui_test/config_selector_ui_test.info.yml
new file mode 100644
index 0000000..08cf2c1
--- /dev/null
+++ b/tests/modules/config_selector_ui_test/config_selector_ui_test.info.yml
@@ -0,0 +1,8 @@
+name: 'Configuration Selector UI Test'
+core: 8.x
+type: module
+package: Testing
+dependencies:
+  - drupal:node
+  - drupal:views
+  - config_selector:config_selector
diff --git a/tests/modules/config_selector_ui_test/config_selector_ui_test.install b/tests/modules/config_selector_ui_test/config_selector_ui_test.install
new file mode 100644
index 0000000..9fe6db6
--- /dev/null
+++ b/tests/modules/config_selector_ui_test/config_selector_ui_test.install
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * The Configuration Selector UI Test module install methods.
+ */
+
+use Drupal\Core\Config\FileStorage;
+
+/**
+ * Implements hook_install().
+ */
+function config_selector_ui_test_install() {
+  // Save ourselves from having to manage a view.
+  $node_optional_dir = drupal_get_path('module', 'node') . '/config/optional';
+  $node_config_storage = new FileStorage($node_optional_dir);
+  $view_data = $node_config_storage->read('views.view.content');
+
+  _config_selector_ui_test_create_new_view('feature_test_1', 'feature_test', 1, FALSE, $view_data);
+  _config_selector_ui_test_create_new_view('feature_test_2', 'feature_test', 2, FALSE, $view_data);
+  _config_selector_ui_test_create_new_view('feature_test_3', 'feature_test', 3, TRUE, $view_data);
+}
+
+/**
+ * Creates a new view based on the supplied params.
+ *
+ * @param string $new_id
+ *   The new view ID.
+ * @param string $feature
+ *   The Configuration Selector feature to add it to.
+ * @param int $priority
+ *   The priority.
+ * @param bool $status
+ *   The status.
+ * @param array $view_data
+ *   The complete data for the view.
+ *
+ * @return \Drupal\views\ViewEntityInterface
+ *   The new view entity.
+ */
+function _config_selector_ui_test_create_new_view($new_id, $feature, $priority, $status, array $view_data) {
+  $view_storage = \Drupal::entityTypeManager()->getStorage('view');
+  $view_data['id'] = $new_id;
+  // Make life simple for testing and ensure the views are removed on uninstall.
+  $view_data['dependencies']['enforced']['module'] = ['config_selector_ui_test'];
+  /** @var \Drupal\views\ViewEntityInterface $view */
+  $view = $view_storage->createFromStorageRecord($view_data);
+  $view->setStatus($status);
+  $view->setThirdPartySetting('config_selector', 'feature', $feature);
+  $view->setThirdPartySetting('config_selector', 'priority', $priority);
+  $view->set('label', $new_id);
+  $view->save();
+  return $view;
+}
