diff --git a/README.txt b/README.txt
index 98e140f..2a08c05 100644
--- a/README.txt
+++ b/README.txt
@@ -1,15 +1,10 @@
 CONTENTS OF THIS FILE
 ---------------------
- * Introduction
  * Requirements
  * Installation
  * Configuration
  * FAQ
 
- INTRODUCTION
- ------------
-Todo
-
 REQUIREMENTS
 ------------
 No other modules required, we're supporting drupal core as a source for creating
@@ -19,7 +14,7 @@ tested.
 INSTALLATION
 ------------
  * Install as you would normally install a contributed drupal module. See:
-  https://drupal.org/documentation/install/modules-themes/modules-7
+  https://www.drupal.org/docs/8/extending-drupal-8/installing-contributed-modules-find-import-enable-configure-drupal-8
   for further information.
 
 CONFIGURATION
@@ -40,3 +35,68 @@ FAQ
 
 Q: Why do the facets disappear after a refresh.
 A: We don't support cached views, change the view to disable caching.
+
+FEATURES
+--------
+
+If you are the developer of a search api backend implementation and want
+to support facets with your service class, too, you'll have to support the
+"search_api_facets" feature. In short, you'll just have to return facet terms
+and counts according to the query's "search_api_facets" option, when executing a
+query.
+In order for the module to be able to tell that your server supports facets,
+you will also have to change your service's supportsFeature() method to
+something like the following:
+
+```
+  public function getSupportedFeatures() {
+    return ['search_api_facets'];
+  }
+```
+
+If you don't do that, there's no way for the facet source to pick up facets.
+
+The "search_api_facets" option looks as follows:
+
+```
+$query->setOption('search_api_facets', [
+  $facet_id => [
+    // The Search API field ID of the field to facet on.
+    'field' => (string),
+    // The maximum number of filters to retrieve for the facet.
+    'limit' => (int),
+    // The facet operator: "and" or "or".
+    'operator' => (string),
+    // The minimum count a filter/value must have to be returned.
+    'min_count' => (int),
+    // Whether to retrieve a facet for "missing" values.
+    'missing' => (bool),
+  ],
+  // …
+]);
+```
+
+The structure of the returned facets array should look like this:
+
+```
+$results->setExtraData('search_api_facets', [
+  $facet_id => [
+    [
+      'count' => (int),
+      'filter' => (string),
+    ],
+    // …
+  ],
+  // …
+]);
+```
+
+A filter is a string with one of the following forms:
+- `"VALUE"`: Filter by the literal value VALUE (always include the quotes, not
+  only for strings).
+- `[VALUE1 VALUE2]`: Filter for a value between VALUE1 and VALUE2. Use
+  parantheses for excluding the border values and square brackets for including
+  them. An asterisk (*) can be used as a wildcard. E.g., (* 0) or [* 0) would be
+  a filter for all negative values.
+- `!`: Filter for items without a value for this field (i.e., the "missing"
+  facet).
diff --git a/facets.install b/facets.install
new file mode 100644
index 0000000..326bada
--- /dev/null
+++ b/facets.install
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Update hooks for the facets module.
+ */
+
+use Drupal\facets\Entity\Facet;
+
+/**
+ * Rename old search api facet sources to the new scheme.
+ *
+ * We changed the way we work with search api facet sources, we're now using the
+ * SearchApiDisplay plugins that search api ships with. This consolidates the
+ * external points for facets, sorts, autocomplete and others. This refactor
+ * made us a better member of the Search API family. It also makes it easier for
+ * other modules that provide a display to support facets, for example:
+ * search_api_page.
+ *
+ * This only works for the 3 default plugins that we previously shipped. So only
+ * views that have a page, block, or rest display. They will get replaced from
+ * views_page:foo to search_api:foo.
+ */
+function facets_update_8001() {
+  /** @var \Drupal\facets\FacetInterface[] $entities */
+  $entities = Facet::loadMultiple();
+  foreach ($entities as $entity) {
+    $facetSourceId = $entity->getFacetSourceId();
+    $old_ids = ['views_page:', 'views_block:', 'views_rest:'];
+
+    foreach ($old_ids as $id) {
+      if (strpos($facetSourceId, $id) !== FALSE) {
+        $new_id = str_replace($id, 'search_api:', $facetSourceId);
+        $entity->setFacetSourceId($new_id);
+        $entity->save();
+      }
+    }
+  }
+}
diff --git a/facets.module b/facets.module
index 4bdd9f6..dcb4469 100644
--- a/facets.module
+++ b/facets.module
@@ -61,8 +61,15 @@ function facets_search_api_query_alter(QueryInterface &$query) {
 
     $search_id = $query->getSearchId();
 
+    // It's safe to hardcode this to the search api scheme because this is in a
+    // search_api_query_alter method. If this generated source is not correct,
+    // implementing the same alter and directly calling
+    // $manager->alterQuery($query, $your_facetsource_id); will fix that.
+    $search_id_array = explode(':', $search_id);
+    $facet_source = 'search_api:' . $search_id_array[1];
+
     // Add the active filters.
-    $facet_manager->alterQuery($query, $search_id);
+    $facet_manager->alterQuery($query, $facet_source);
   }
 }
 
@@ -88,7 +95,7 @@ function facets_entity_presave(EntityInterface $entity) {
         // Check if the current display is also a facet source plugin and that
         // is removed from the view. We use the double underscore here to make
         // sure that we use core convention of "plugin:derived_plugin".
-        $facets_source_plugin_id = 'views_page:' . $entity->id() . '__' . $display['id'];
+        $facets_source_plugin_id = 'search_api:' . $entity->id() . '__' . $display['id'];
         if (array_key_exists($facets_source_plugin_id, $definitions) && !array_key_exists($k, $entity->get('display'))) {
           $entity_id = str_replace(':', '__', $facets_source_plugin_id);
           $source_entity = FacetSource::load($entity_id);
@@ -152,7 +159,7 @@ function facets_entity_predelete(EntityInterface $entity) {
     }
 
     foreach ($definitions as $plugin_id => $definition) {
-      if (strpos($plugin_id, 'views_page:' . $entity->id() . '__') !== FALSE) {
+      if (strpos($plugin_id, 'search_api:' . $entity->id() . '__') !== FALSE) {
         try {
           $facetManager = \Drupal::getContainer()->get('facets.manager');
         } catch (ServiceNotFoundException $e) {
diff --git a/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php b/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php
index 0538253..39fe5ee 100644
--- a/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php
+++ b/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php
@@ -3,6 +3,7 @@
 namespace Drupal\core_search_facets\Plugin\facets\facet_source;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface;
 use Drupal\facets\FacetInterface;
 use Drupal\facets\FacetSource\FacetSourcePluginBase;
@@ -110,9 +111,9 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
   public function getPath() {
     $search_page = $this->request->attributes->get('entity');
     if ($search_page instanceof SearchPageInterface) {
-      return '/search/' . $search_page->getPath();
+      return Url::fromUserInput('/search/' . $search_page->getPath());
     }
-    return '/';
+    return Url::fromUserInput('/');
   }
 
   /**
diff --git a/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSourceDeriver.php b/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSourceDeriver.php
index ba9d5f5..4cd862f 100644
--- a/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSourceDeriver.php
+++ b/modules/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSourceDeriver.php
@@ -67,9 +67,9 @@ class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase {
             'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name,
             'label' => $this->t('Core Search Page: %page_name', ['%page_name' => $page->get('label')]),
             'description' => $this->t('Provides a facet source.'),
+            'display_id' => $machine_name,
           ] + $base_plugin_definition;
         }
-        uasort($plugin_derivatives, array($this, 'compareDerivatives'));
 
         $this->derivatives[$base_plugin_id] = $plugin_derivatives;
       }
diff --git a/modules/facets_summary/tests/src/Functional/IntegrationTest.php b/modules/facets_summary/tests/src/Functional/IntegrationTest.php
index f893354..3e8b7fb 100644
--- a/modules/facets_summary/tests/src/Functional/IntegrationTest.php
+++ b/modules/facets_summary/tests/src/Functional/IntegrationTest.php
@@ -74,7 +74,7 @@ class IntegrationTest extends FacetsTestBase {
     $values = [
       'name' => 'Owl',
       'id' => 'owl',
-      'facet_source_id' => 'views_page:search_api_test_view__page_1',
+      'facet_source_id' => 'search_api:search_api_test_view__page_1',
     ];
     $this->drupalPostForm('admin/config/search/facets/add-facet-summary', $values, 'Save');
     $this->drupalPostForm(NULL, [], 'Save');
diff --git a/modules/rest_facets/rest_facets.info.yml b/modules/rest_facets/rest_facets.info.yml
index 6d53c5a..51181e1 100644
--- a/modules/rest_facets/rest_facets.info.yml
+++ b/modules/rest_facets/rest_facets.info.yml
@@ -5,6 +5,7 @@ core: 8.x
 package: Search
 dependencies:
   - facets:facets
+  - drupal:rest
 test_dependencies:
   - search_api:search_api
   - facets:facets
diff --git a/modules/rest_facets/src/Plugin/views/style/FacetsSerializer.php b/modules/rest_facets/src/Plugin/views/style/FacetsSerializer.php
index 5c4a1d3..fd0b239 100644
--- a/modules/rest_facets/src/Plugin/views/style/FacetsSerializer.php
+++ b/modules/rest_facets/src/Plugin/views/style/FacetsSerializer.php
@@ -24,7 +24,7 @@ class FacetsSerializer extends Serializer {
   /**
    * Tha facet manager.
    *
-   * @var DefaultFacetManager
+   * @var \Drupal\facets\FacetManager\DefaultFacetManager
    */
   protected $facetsManager;
 
@@ -80,7 +80,7 @@ class FacetsSerializer extends Serializer {
     }
 
     // Processing facets.
-    $facetsource_id = "views_page:{$this->view->id()}__{$this->view->getDisplay()->display['id']}";
+    $facetsource_id = "search_api:{$this->view->id()}__{$this->view->getDisplay()->display['id']}";
     $facets = $this->facetsManager->getFacetsByFacetSourceId($facetsource_id);
     $this->facetsManager->updateResults($facetsource_id);
 
diff --git a/modules/rest_facets/tests/src/Functional/RestIntegrationTest.php b/modules/rest_facets/tests/src/Functional/RestIntegrationTest.php
index addac17..48a0dac 100644
--- a/modules/rest_facets/tests/src/Functional/RestIntegrationTest.php
+++ b/modules/rest_facets/tests/src/Functional/RestIntegrationTest.php
@@ -79,6 +79,7 @@ class RestIntegrationTest extends FacetsTestBase {
     $values['facet_sorting[display_value_widget_order][status]'] = FALSE;
     $values['facet_sorting[active_widget_order][status]'] = FALSE;
     $values['facet_settings[query_operator]'] = 'or';
+    $values['facet_settings[only_visible_when_facet_source_is_visible]'] = FALSE;
 
     $this->drupalPostForm(NULL, $values, $this->t('Save'));
 
diff --git a/src/FacetSource/FacetSourceDeriverBase.php b/src/FacetSource/FacetSourceDeriverBase.php
index e987112..9472bb1 100644
--- a/src/FacetSource/FacetSourceDeriverBase.php
+++ b/src/FacetSource/FacetSourceDeriverBase.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\facets\FacetSource;
 
-use Drupal\Core\Entity\EntityTypeManager;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -63,7 +62,7 @@ abstract class FacetSourceDeriverBase implements ContainerDeriverInterface {
   /**
    * Retrieves the entity manager.
    *
-   * @return \Drupal\Core\Entity\EntityTypeManager
+   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
    *   The entity manager.
    */
   public function getEntityTypeManager() {
@@ -92,23 +91,6 @@ abstract class FacetSourceDeriverBase implements ContainerDeriverInterface {
   }
 
   /**
-   * Compares two plugin definitions according to their labels.
-   *
-   * @param array $a
-   *   A plugin definition, with at least a "label" key.
-   * @param array $b
-   *   Another plugin definition.
-   *
-   * @return int
-   *   An integer less than, equal to, or greater than zero if the first
-   *   argument is considered to be respectively less than, equal to, or greater
-   *   than the second.
-   */
-  public function compareDerivatives(array $a, array $b) {
-    return strnatcasecmp($a['label'], $b['label']);
-  }
-
-  /**
    * Sets search api's display plugin manager.
    *
    * @param \Drupal\search_api\Display\DisplayPluginManager $search_api_display_plugin_manager
diff --git a/src/FacetSource/FacetSourcePluginInterface.php b/src/FacetSource/FacetSourcePluginInterface.php
index 698b365..733ad79 100644
--- a/src/FacetSource/FacetSourcePluginInterface.php
+++ b/src/FacetSource/FacetSourcePluginInterface.php
@@ -40,10 +40,11 @@ interface FacetSourcePluginInterface extends PluginFormInterface, DependentPlugi
   public function getQueryTypesForFacet(FacetInterface $facet);
 
   /**
-   * Returns the path where a facet should link to.
+   * Returns the url of the facet source, used to build the facet url.
    *
-   * @return string
-   *   The path of the facet.
+   * @return \Drupal\Core\Url
+   *   The url object for the facet if it's set, a url object to the current
+   *   page otherwise.
    */
   public function getPath();
 
diff --git a/src/FacetSource/FacetSourcePluginManager.php b/src/FacetSource/FacetSourcePluginManager.php
index 4f4146a..9731e2c 100644
--- a/src/FacetSource/FacetSourcePluginManager.php
+++ b/src/FacetSource/FacetSourcePluginManager.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\facets\FacetSource;
 
+use Drupal\Component\Plugin\Exception\PluginException;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
@@ -22,4 +23,30 @@ class FacetSourcePluginManager extends DefaultPluginManager {
     parent::__construct('Plugin/facets/facet_source', $namespaces, $module_handler, 'Drupal\facets\FacetSource\FacetSourcePluginInterface', 'Drupal\facets\Annotation\FacetsFacetSource');
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    parent::processDefinition($definition, $plugin_id);
+
+    // At the very least - we need to have an ID in the definition of the
+    // plugin.
+    if (!isset($definition['id'])) {
+      throw new PluginException(sprintf('The facet source plugin %s must define the id property.', $plugin_id));
+    }
+
+    // If we're checking the search api plugin, only try to add it if search api
+    // is enabled.
+    if ($definition['id'] === 'search_api' && !$this->moduleHandler->moduleExists('search_api')) {
+      return;
+    }
+
+    // Check that other required labels are available.
+    foreach (['display_id', 'label'] as $required_property) {
+      if (empty($definition[$required_property])) {
+        throw new PluginException(sprintf('The facet source plugin %s must define the %s property.', $plugin_id, $required_property));
+      }
+    }
+  }
+
 }
diff --git a/src/Form/FacetForm.php b/src/Form/FacetForm.php
index 0a052bd..3bac7df 100644
--- a/src/Form/FacetForm.php
+++ b/src/Form/FacetForm.php
@@ -6,6 +6,7 @@ use Drupal\Component\Utility\Html;
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay;
 use Drupal\facets\Processor\ProcessorInterface;
 use Drupal\facets\Processor\ProcessorPluginManager;
 use Drupal\facets\UrlProcessor\UrlProcessorInterface;
@@ -373,7 +374,7 @@ class FacetForm extends EntityForm {
       '#options' => [0 => $this->t('No limit')] + array_combine($hard_limit_options, $hard_limit_options),
       '#description' => $this->t('Display no more than this number of facet items.'),
     ];
-    if (strpos($facet->getFacetSourceId(), 'views_') === FALSE) {
+    if (!$facet->getFacetSource() instanceof SearchApiDisplay) {
       $form['facet_settings']['hard_limit']['#disabled'] = TRUE;
       $form['facet_settings']['hard_limit']['#description'] .= '<br />';
       $form['facet_settings']['hard_limit']['#description'] .= $this->t('This setting only works with Search API based facets.');
@@ -391,7 +392,7 @@ class FacetForm extends EntityForm {
       '#title' => $this->t('Use hierarchy'),
       '#default_value' => $facet->getUseHierarchy(),
     ];
-    if (strpos($facet->getFacetSourceId(), 'views_') === FALSE) {
+    if (!$facet->getFacetSource() instanceof SearchApiDisplay) {
       $form['facet_settings']['use_hierarchy']['#disabled'] = TRUE;
       $form['facet_settings']['use_hierarchy']['#description'] = $this->t('This setting only works with Search API based facets.');
     }
@@ -439,7 +440,7 @@ class FacetForm extends EntityForm {
       '#maxlength' => 4,
       '#required' => TRUE,
     ];
-    if (strpos($facet->getFacetSourceId(), 'views_') === FALSE) {
+    if (!$facet->getFacetSource() instanceof SearchApiDisplay) {
       $form['facet_settings']['min_count']['#disabled'] = TRUE;
       $form['facet_settings']['min_count']['#description'] .= '<br />';
       $form['facet_settings']['min_count']['#description'] .= $this->t('This setting only works with Search API based facets.');
diff --git a/src/Plugin/facets/facet_source/SearchApiBaseFacetSource.php b/src/Plugin/facets/facet_source/SearchApiBaseFacetSource.php
deleted file mode 100644
index 2a270db..0000000
--- a/src/Plugin/facets/facet_source/SearchApiBaseFacetSource.php
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-
-namespace Drupal\facets\Plugin\facets\facet_source;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\facets\Exception\InvalidQueryTypeException;
-use Drupal\facets\FacetInterface;
-use Drupal\search_api\Backend\BackendInterface;
-use Drupal\facets\FacetSource\FacetSourcePluginBase;
-use Drupal\search_api\FacetsQueryTypeMappingInterface;
-use Drupal\search_api\Utility\QueryHelper;
-use Drupal\facets\QueryType\QueryTypePluginManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * A base class for Search API facet sources.
- */
-abstract class SearchApiBaseFacetSource extends FacetSourcePluginBase {
-
-  /**
-   * The search index.
-   *
-   * @var \Drupal\search_api\IndexInterface
-   */
-  protected $index;
-
-  /**
-   * The search result cache.
-   *
-   * @var \Drupal\search_api\Utility\QueryHelper
-   */
-  protected $searchApiQueryHelper;
-
-  /**
-   * Constructs a SearchApiBaseFacetSource 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.
-   * @param \Drupal\facets\QueryType\QueryTypePluginManager $query_type_plugin_manager
-   *   The query type plugin manager.
-   * @param \Drupal\search_api\Utility\QueryHelper $search_results_cache
-   *   The query type plugin manager.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, QueryTypePluginManager $query_type_plugin_manager, QueryHelper $search_results_cache) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager);
-    // Since defaultConfiguration() depends on the plugin definition, we need to
-    // override the constructor and set the definition property before calling
-    // that method.
-    $this->pluginDefinition = $plugin_definition;
-    $this->pluginId = $plugin_id;
-    $this->configuration = $configuration;
-    $this->searchApiQueryHelper = $search_results_cache;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('plugin.manager.facets.query_type'),
-      $container->get('search_api.query_helper')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-
-    $form['field_identifier'] = [
-      '#type' => 'select',
-      '#options' => $this->getFields(),
-      '#title' => $this->t('Field'),
-      '#description' => $this->t('The field from the selected facet source which contains the data to build a facet for.<br> The field types supported are <strong>boolean</strong>, <strong>date</strong>, <strong>decimal</strong>, <strong>integer</strong> and <strong>string</strong>.'),
-      '#required' => TRUE,
-      '#default_value' => $this->facet->getFieldIdentifier(),
-    ];
-
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFields() {
-    $indexed_fields = [];
-    $fields = $this->index->getFields();
-    // Get the Search API Server.
-    $server = $this->index->getServerInstance();
-    // Get the Search API Backend.
-    $backend = $server->getBackend();
-    foreach ($fields as $field) {
-      $query_types = $this->getQueryTypesForDataType($backend, $field->getDataTypePlugin()->getPluginId());
-      if (!empty($query_types)) {
-        $indexed_fields[$field->getFieldIdentifier()] = $field->getLabel() . ' (' . $field->getPropertyPath() . ')';
-      }
-    }
-    return $indexed_fields;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQueryTypesForFacet(FacetInterface $facet) {
-    // Get our Facets Field Identifier, which is equal to the Search API Field
-    // identifier.
-    $field_id = $facet->getFieldIdentifier();
-    // Get the Search API Server.
-    $server = $this->index->getServerInstance();
-    // Get the Search API Backend.
-    $backend = $server->getBackend();
-
-    $fields = $this->index->getFields();
-    foreach ($fields as $field) {
-      if ($field->getFieldIdentifier() == $field_id) {
-        return $this->getQueryTypesForDataType($backend, $field->getType());
-      }
-    }
-
-    throw new InvalidQueryTypeException("No available query types were found for facet {$facet->getName()}");
-  }
-
-  /**
-   * Retrieves the query types for a specified data type.
-   *
-   * Backend plugins can use this method to override the default query types
-   * provided by the Search API with backend-specific ones that better use
-   * features of that backend.
-   *
-   * @param \Drupal\search_api\Backend\BackendInterface $backend
-   *   The backend that we want to get the query types for.
-   * @param string $data_type_plugin_id
-   *   The identifier of the data type.
-   *
-   * @return string[]
-   *   An associative array with the plugin IDs of allowed query types, keyed by
-   *   the generic name of the query_type.
-   *
-   * @see hook_facets_search_api_query_type_mapping_alter()
-   */
-  public function getQueryTypesForDataType(BackendInterface $backend, $data_type_plugin_id) {
-    $query_types = [];
-    $query_types['string'] = 'search_api_string';
-
-    // Add additional query types for specific data types.
-    switch ($data_type_plugin_id) {
-      case 'date':
-        $query_types['date'] = 'search_api_date';
-        break;
-
-      case 'decimal':
-      case 'integer':
-        $query_types['numeric'] = 'search_api_granular';
-        break;
-
-    }
-
-    // Find out if the backend implemented the Interface to retrieve specific
-    // query types for the supported data_types.
-    if ($backend instanceof FacetsQueryTypeMappingInterface) {
-      // If the input arrays have the same string keys, then the later value
-      // for that key will overwrite the previous one. If, however, the arrays
-      // contain numeric keys, the later value will not overwrite the original
-      // value, but will be appended.
-      $query_types = array_merge($query_types, $backend->getQueryTypesForDataType($data_type_plugin_id));
-    }
-    // Add it to a variable so we can pass it by reference. Alter hook complains
-    // due to the property of the backend object is not passable by reference.
-    $backend_plugin_id = $backend->getPluginId();
-
-    // Let modules alter this mapping.
-    \Drupal::moduleHandler()->alter('facets_search_api_query_type_mapping', $backend_plugin_id, $query_types);
-
-    return $query_types;
-  }
-
-}
diff --git a/src/Plugin/facets/facet_source/SearchApiDisplay.php b/src/Plugin/facets/facet_source/SearchApiDisplay.php
new file mode 100644
index 0000000..16ba8a9
--- /dev/null
+++ b/src/Plugin/facets/facet_source/SearchApiDisplay.php
@@ -0,0 +1,309 @@
+<?php
+
+namespace Drupal\facets\Plugin\facets\facet_source;
+
+use Drupal\Component\Plugin\DependentPluginInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\facets\Exception\InvalidQueryTypeException;
+use Drupal\facets\FacetInterface;
+use Drupal\facets\FacetSource\FacetSourcePluginBase;
+use Drupal\facets\QueryType\QueryTypePluginManager;
+use Drupal\search_api\Backend\BackendInterface;
+use Drupal\search_api\Display\DisplayPluginManager;
+use Drupal\search_api\FacetsQueryTypeMappingInterface;
+use Drupal\search_api\Query\ResultSetInterface;
+use Drupal\search_api\Utility\QueryHelper;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides a facet source based on a Search API display.
+ *
+ * @FacetsFacetSource(
+ *   id = "search_api",
+ *   deriver = "Drupal\facets\Plugin\facets\facet_source\SearchApiDisplayDeriver"
+ * )
+ */
+class SearchApiDisplay extends FacetSourcePluginBase {
+
+  /**
+   * The search index the query should is executed on.
+   *
+   * @var \Drupal\search_api\IndexInterface
+   */
+  protected $index;
+
+  /**
+   * The display plugin manager.
+   *
+   * @var \Drupal\search_api\Display\DisplayPluginManager
+   */
+  protected $displayPluginManager;
+
+  /**
+   * The search result cache.
+   *
+   * @var \Drupal\search_api\Utility\QueryHelper
+   */
+  protected $searchApiQueryHelper;
+
+  /**
+   * The current request.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * Constructs a SearchApiBaseFacetSource 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.
+   * @param \Drupal\facets\QueryType\QueryTypePluginManager $query_type_plugin_manager
+   *   The query type plugin manager.
+   * @param \Drupal\search_api\Utility\QueryHelper $search_results_cache
+   *   The query type plugin manager.
+   * @param \Drupal\search_api\Display\DisplayPluginManager $display_plugin_manager
+   *   The display plugin manager.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object for the current request.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, QueryTypePluginManager $query_type_plugin_manager, QueryHelper $search_results_cache, DisplayPluginManager $display_plugin_manager, Request $request) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager);
+
+    $this->searchApiQueryHelper = $search_results_cache;
+    $this->displayPluginManager = $display_plugin_manager;
+    $this->request = $request;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('plugin.manager.facets.query_type'),
+      $container->get('search_api.query_helper'),
+      $container->get('plugin.manager.search_api.display'),
+      $container->get('request_stack')->getMasterRequest()
+    );
+  }
+
+  /**
+   * Retrieves the Search API index for this facet source.
+   *
+   * @return \Drupal\search_api\IndexInterface
+   *   The search index.
+   */
+  public function getIndex() {
+    if ($this->index === NULL) {
+      $this->index = $this->getDisplay()->getIndex();
+    }
+
+    return $this->index;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPath() {
+    // The implementation in search api tells us that this is a url object only
+    // if a path is defined, and null if that isn't done. This means that we
+    // have to check for this + create our own Url object if that's needed.
+    if ($this->getDisplay()->getUrl() instanceof Url) {
+      return $this->getDisplay()->getUrl();
+    }
+
+    return Url::createFromRequest($this->request);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fillFacetsWithResults(array $facets) {
+    $search_id = $this->getDisplay()->getPluginId();
+
+    // Check if the results for this search id are already populated in the
+    // query helper. This is usually the case for views displays that are
+    // rendered on the same page, such as views_page.
+    $results = $this->searchApiQueryHelper->getResults($search_id);
+
+    if (!$results instanceof ResultSetInterface) {
+      return;
+    }
+
+    // Get our facet data.
+    $facet_results = $results->getExtraData('search_api_facets');
+    if ($facet_results === []) {
+      return;
+    }
+
+    // Loop over each facet and execute the build method from the given
+    // query type.
+    foreach ($facets as $facet) {
+      $configuration = array(
+        'query' => NULL,
+        'facet' => $facet,
+        'results' => isset($facet_results[$facet->getFieldIdentifier()]) ? $facet_results[$facet->getFieldIdentifier()] : [],
+      );
+
+      // Get the Facet Specific Query Type so we can process the results
+      // using the build() function of the query type.
+      $query_type = $this->queryTypePluginManager->createInstance($facet->getQueryType(), $configuration);
+      $query_type->build();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isRenderedInCurrentRequest() {
+    return $this->getDisplay()->isRenderedInCurrentRequest();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['field_identifier'] = [
+      '#type' => 'select',
+      '#options' => $this->getFields(),
+      '#title' => $this->t('Field'),
+      '#description' => $this->t('The field from the selected facet source which contains the data to build a facet for.<br> The field types supported are <strong>boolean</strong>, <strong>date</strong>, <strong>decimal</strong>, <strong>integer</strong> and <strong>string</strong>.'),
+      '#required' => TRUE,
+      '#default_value' => $this->facet->getFieldIdentifier(),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFields() {
+    $indexed_fields = [];
+    $index = $this->getIndex();
+
+    $fields = $index->getFields();
+    $server = $index->getServerInstance();
+    $backend = $server->getBackend();
+
+    foreach ($fields as $field) {
+      $data_type_plugin_id = $field->getDataTypePlugin()->getPluginId();
+      $query_types = $this->getQueryTypesForDataType($backend, $data_type_plugin_id);
+      if (!empty($query_types)) {
+        $indexed_fields[$field->getFieldIdentifier()] = $field->getLabel() . ' (' . $field->getPropertyPath() . ')';
+      }
+    }
+
+    return $indexed_fields;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQueryTypesForFacet(FacetInterface $facet) {
+    // Get our Facets Field Identifier, which is equal to the Search API Field
+    // identifier.
+    $field_id = $facet->getFieldIdentifier();
+    /** @var \Drupal\search_api\IndexInterface $index */
+    $index = $this->getIndex();
+    // Get the Search API Server.
+    $server = $index->getServerInstance();
+    // Get the Search API Backend.
+    $backend = $server->getBackend();
+
+    $fields = $index->getFields();
+    foreach ($fields as $field) {
+      if ($field->getFieldIdentifier() == $field_id) {
+        return $this->getQueryTypesForDataType($backend, $field->getType());
+      }
+    }
+
+    throw new InvalidQueryTypeException("No available query types were found for facet {$facet->getName()}");
+  }
+
+  /**
+   * Retrieves the query types for a specified data type.
+   *
+   * Backend plugins can use this method to override the default query types
+   * provided by the Search API with backend-specific ones that better use
+   * features of that backend.
+   *
+   * @param \Drupal\search_api\Backend\BackendInterface $backend
+   *   The backend that we want to get the query types for.
+   * @param string $data_type_plugin_id
+   *   The identifier of the data type.
+   *
+   * @return string[]
+   *   An associative array with the plugin IDs of allowed query types, keyed by
+   *   the generic name of the query_type.
+   *
+   * @see hook_facets_search_api_query_type_mapping_alter()
+   */
+  public function getQueryTypesForDataType(BackendInterface $backend, $data_type_plugin_id) {
+    $query_types = [];
+    $query_types['string'] = 'search_api_string';
+
+    // Add additional query types for specific data types.
+    switch ($data_type_plugin_id) {
+      case 'date':
+        $query_types['date'] = 'search_api_date';
+        break;
+
+      case 'decimal':
+      case 'integer':
+        $query_types['numeric'] = 'search_api_granular';
+        break;
+
+    }
+
+    // Find out if the backend implemented the Interface to retrieve specific
+    // query types for the supported data_types.
+    if ($backend instanceof FacetsQueryTypeMappingInterface) {
+      $mapping = [
+        $data_type_plugin_id => &$query_types,
+      ];
+      $backend->alterFacetQueryTypeMapping($mapping);
+    }
+    // Add it to a variable so we can pass it by reference. Alter hook complains
+    // due to the property of the backend object is not passable by reference.
+    $backend_plugin_id = $backend->getPluginId();
+
+    // Let modules alter this mapping.
+    \Drupal::moduleHandler()
+      ->alter('facets_search_api_query_type_mapping', $backend_plugin_id, $query_types);
+
+    return $query_types;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    $display = $this->getDisplay();
+    if ($display instanceof DependentPluginInterface) {
+      return $display->calculateDependencies();
+    }
+    return [];
+  }
+
+  /**
+   * Retrieves the Search API display plugin associated with this facet source.
+   *
+   * @return \Drupal\search_api\Display\DisplayInterface
+   *   The Search API display plugin associated with this facet source.
+   */
+  protected function getDisplay() {
+    return $this->displayPluginManager
+      ->createInstance($this->pluginDefinition['display_id']);
+  }
+
+}
diff --git a/src/Plugin/facets/facet_source/SearchApiDisplayDeriver.php b/src/Plugin/facets/facet_source/SearchApiDisplayDeriver.php
new file mode 100644
index 0000000..62aa12d
--- /dev/null
+++ b/src/Plugin/facets/facet_source/SearchApiDisplayDeriver.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Drupal\facets\Plugin\facets\facet_source;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\facets\FacetSource\FacetSourceDeriverBase;
+
+/**
+ * Derives a facet source plugin definition for every Search API display plugin.
+ *
+ * This facet source supports all search api display sources.
+ *
+ * @see \Drupal\facets\Plugin\facets\facet_source\SearchApi
+ */
+class SearchApiDisplayDeriver extends FacetSourceDeriverBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    $base_plugin_id = $base_plugin_definition['id'];
+    $plugin_derivatives = array();
+
+    $display_plugin_manager = $this->getSearchApiDisplayPluginManager();
+    foreach ($display_plugin_manager->getDefinitions() as $display_id => $display_definition) {
+      // If 'index' is not set on the plugin, we can't load the index.
+      if (!isset($display_definition['index'])) {
+        continue;
+      }
+
+      $display = $display_plugin_manager->createInstance($display_id);
+
+      $supports_facets = $display->getIndex()
+        ->getServerInstance()
+        ->supportsFeature('search_api_facets');
+
+      // If facets are not supported by the server, don't actually add this to
+      // the list of plugins.
+      if (!$supports_facets) {
+        continue;
+      }
+
+      $machine_name = $display->getDerivativeId();
+      $plugin_derivatives[$machine_name] = [
+        'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name,
+        'display_id' => $display_id,
+        'label' => $display->label(),
+        'description' => $display->getDescription(),
+      ] + $base_plugin_definition;
+    }
+
+    $this->derivatives[$base_plugin_id] = $plugin_derivatives;
+    return $this->derivatives[$base_plugin_id];
+  }
+
+}
diff --git a/src/Plugin/facets/facet_source/SearchApiViews.php b/src/Plugin/facets/facet_source/SearchApiViews.php
deleted file mode 100644
index 755949b..0000000
--- a/src/Plugin/facets/facet_source/SearchApiViews.php
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-
-namespace Drupal\facets\Plugin\facets\facet_source;
-
-use Drupal\facets\FacetSource\SearchApiFacetSourceInterface;
-use Drupal\search_api\Plugin\views\query\SearchApiQuery;
-use Drupal\search_api\Query\ResultSetInterface;
-use Drupal\views\Entity\View;
-use Drupal\views\Views;
-
-/**
- * A facet source to support search api views trough display plugins.
- *
- * @FacetsFacetSource(
- *   id = "views_page",
- *   deriver = "Drupal\facets\Plugin\facets\facet_source\SearchApiViewsDeriver"
- * )
- */
-class SearchApiViews extends SearchApiBaseFacetSource implements SearchApiFacetSourceInterface {
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeManager|null
-   */
-  protected $entityTypeManager;
-
-  /**
-   * The typed data manager.
-   *
-   * @var \Drupal\Core\TypedData\TypedDataManager|null
-   */
-  protected $typedDataManager;
-
-  /**
-   * The config factory.
-   *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface|null
-   */
-  protected $configFactory;
-
-  /**
-   * The search index the query should is executed on.
-   *
-   * @var \Drupal\search_api\IndexInterface
-   */
-  protected $index;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, $query_type_plugin_manager, $search_results_cache) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager, $search_results_cache);
-
-    // Load facet plugin definition and depending on those settings; load the
-    // corresponding view with the correct view with the correct display set.
-    // Get that display's query so we can check if this is a Search API based
-    // view.
-    $view = Views::getView($plugin_definition['view_id']);
-    if (!empty($view)) {
-      $view->setDisplay($plugin_definition['view_display']);
-      $query = $view->getQuery();
-
-      // Only add the index if the $query is a Search API Query.
-      if ($query instanceof SearchApiQuery) {
-        // Set the Search API Index.
-        $this->index = $query->getIndex();
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPath() {
-    $display = View::load($this->pluginDefinition['view_id'])->getDisplay($this->pluginDefinition['view_display']);
-    switch ($display['display_plugin']) {
-      case 'page':
-        $view = Views::getView($this->pluginDefinition['view_id']);
-        $view->setDisplay($this->pluginDefinition['view_display']);
-        return '/' . $view->getDisplay()->getPath();
-
-      case 'block':
-      default:
-        $current_path = \Drupal::service('path.current')->getPath();
-        if (\Drupal::moduleHandler()->moduleExists('path')) {
-          return \Drupal::service('path.alias_manager')->getAliasByPath($current_path);
-        }
-        else {
-          return $current_path;
-        }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function fillFacetsWithResults(array $facets) {
-    // Check if there are results in the static cache.
-    $results = $this->searchApiQueryHelper->getResults($this->pluginId);
-
-    // If our results are not there, execute the view to get the results.
-    if ($results === NULL) {
-      // If there are no results, execute the view. and check for results again!
-      $view = Views::getView($this->pluginDefinition['view_id']);
-      $view->setDisplay($this->pluginDefinition['view_display']);
-      $view->execute();
-      $results = $this->searchApiQueryHelper->getResults($this->pluginId);
-    }
-
-    // Get the results from the cache. It is possible it still errored out.
-    if ($results instanceof ResultSetInterface) {
-      // Get our facet data.
-      $facet_results = $results->getExtraData('search_api_facets');
-      if ($facet_results === []) {
-        return;
-      }
-
-      // Loop over each facet and execute the build method from the given
-      // query type.
-      foreach ($facets as $facet) {
-        $configuration = array(
-          'query' => NULL,
-          'facet' => $facet,
-          'results' => isset($facet_results[$facet->getFieldIdentifier()]) ? $facet_results[$facet->getFieldIdentifier()] : [],
-        );
-
-        // Get the Facet Specific Query Type so we can process the results
-        // using the build() function of the query type.
-        $query_type = $this->queryTypePluginManager->createInstance($facet->getQueryType(), $configuration);
-        $query_type->build();
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isRenderedInCurrentRequest() {
-    $display = View::load($this->pluginDefinition['view_id'])->getDisplay($this->pluginDefinition['view_display']);
-    switch ($display['display_plugin']) {
-      case 'rest_export':
-      case 'page':
-        $request = \Drupal::requestStack()->getMasterRequest();
-        if ($request->attributes->get('_controller') === 'Drupal\views\Routing\ViewPageController::handle') {
-          list(, $view) = explode(':', $this->getPluginId());
-          list($search_api_view_id, $search_api_view_display) = explode('__', $view);
-
-          if ($request->attributes->get('view_id') == $search_api_view_id && $request->attributes->get('display_id') == $search_api_view_display) {
-            return TRUE;
-          }
-        }
-        return FALSE;
-
-      case 'block':
-        // There is no way to know if a block is embedded on a page, because
-        // blocks can be rendered in isolation (see big_pipe, esi, ...). To be
-        // sure we're not disclosing information we're not sure about, we always
-        // return false.
-        return FALSE;
-    }
-    return FALSE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getIndex() {
-    return $this->index;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function calculateDependencies() {
-    $plugin_id_array = explode(':', $this->pluginId);
-    list($view_id,) = explode('__', $plugin_id_array[1]);
-    return [
-      'config' => ['views.view.' . $view_id],
-      'module' => ['views'],
-    ];
-  }
-
-}
diff --git a/src/Plugin/facets/facet_source/SearchApiViewsDeriver.php b/src/Plugin/facets/facet_source/SearchApiViewsDeriver.php
deleted file mode 100644
index b6b6632..0000000
--- a/src/Plugin/facets/facet_source/SearchApiViewsDeriver.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace Drupal\facets\Plugin\facets\facet_source;
-
-use Drupal\facets\FacetSource\FacetSourceDeriverBase;
-
-/**
- * Derives a facet source plugin definition for every Search API display plugin.
- *
- * This facet source supports all search api display sources.
- *
- * @see \Drupal\facets\Plugin\facets\facet_source\SearchApi
- */
-class SearchApiViewsDeriver extends FacetSourceDeriverBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDerivativeDefinitions($base_plugin_definition) {
-    $base_plugin_id = $base_plugin_definition['id'];
-
-    $search_api_displays = $this->getSearchApiDisplayPluginManager();
-
-    $plugin_derivatives = array();
-    foreach ($search_api_displays->getDefinitions() as $display) {
-      // Avoid providing corrupted displays.
-      if (isset($display['view_id']) && isset($display['view_display']) && isset($display['label'])) {
-        $machine_name = $display['view_id'] . '__' . $display['view_display'];
-
-        $plugin_derivatives[$machine_name] = [
-          'id' => $base_plugin_id . ':' . $machine_name,
-          'label' => $display['label'],
-          'description' => $this->t('Provides a facet source.'),
-          'view_id' => $display['view_id'],
-          'view_display' => $display['view_display'],
-        ] + $base_plugin_definition;
-
-        $arguments = [
-          '%view' => $display['label'],
-          '%display' => $display['view_display'],
-        ];
-        $sources[] = $this->t('Search API view: %view, display: %display', $arguments);
-      }
-    }
-
-    uasort($plugin_derivatives, array($this, 'compareDerivatives'));
-
-    $this->derivatives[$base_plugin_id] = $plugin_derivatives;
-    return $this->derivatives[$base_plugin_id];
-  }
-
-}
diff --git a/src/Plugin/facets/processor/TranslateEntityProcessor.php b/src/Plugin/facets/processor/TranslateEntityProcessor.php
index 415404d..4b1591e 100644
--- a/src/Plugin/facets/processor/TranslateEntityProcessor.php
+++ b/src/Plugin/facets/processor/TranslateEntityProcessor.php
@@ -7,7 +7,7 @@ use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 use Drupal\facets\FacetInterface;
-use Drupal\facets\FacetSource\SearchApiFacetSourceInterface;
+use Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay;
 use Drupal\facets\Processor\BuildProcessorInterface;
 use Drupal\facets\Processor\ProcessorPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -92,7 +92,7 @@ class TranslateEntityProcessor extends ProcessorPluginBase implements BuildProce
     $source = $facet->getFacetSource();
 
     // Support multiple entity types when using Search API.
-    if ($source instanceof SearchApiFacetSourceInterface) {
+    if ($source instanceof SearchApiDisplay) {
 
       $field_id = $facet->getFieldIdentifier();
 
diff --git a/src/Plugin/facets/url_processor/QueryString.php b/src/Plugin/facets/url_processor/QueryString.php
index aacc644..5e9b067 100644
--- a/src/Plugin/facets/url_processor/QueryString.php
+++ b/src/Plugin/facets/url_processor/QueryString.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\facets\Plugin\facets\url_processor;
 
-use Drupal\Core\Url;
 use Drupal\facets\FacetInterface;
 use Drupal\facets\UrlProcessor\UrlProcessorPluginBase;
 use Symfony\Component\HttpFoundation\Request;
@@ -68,15 +67,11 @@ class QueryString extends UrlProcessorPluginBase {
     // Set the url alias from the the facet object.
     $this->urlAlias = $facet->getUrlAlias();
 
-    $request = $this->request;
-    if ($facet->getFacetSource()->getPath()) {
-      $request = Request::create($facet->getFacetSource()->getPath());
-    }
+    $url = $facet->getFacetSource()->getPath();
+    $url->setOption('attributes', ['rel' => 'nofollow']);
+
     /** @var \Drupal\facets\Result\ResultInterface[] $results */
     foreach ($results as &$result) {
-      // Reset the URL for each result.
-      $url = Url::createFromRequest($request);
-      $url->setOption('attributes', ['rel' => 'nofollow']);
       // Sets the url for children.
       if ($children = $result->getChildren()) {
         $this->buildUrls($facet, $children);
@@ -133,12 +128,12 @@ class QueryString extends UrlProcessorPluginBase {
 
       $result_get_params->set($this->filterKey, array_values($filter_params));
 
-      $url = clone $url;
+      $new_url = clone $url;
       if ($result_get_params->all() !== [$this->filterKey => []]) {
-        $url->setOption('query', $result_get_params->all());
+        $new_url->setOption('query', $result_get_params->all());
       }
 
-      $result->setUrl($url);
+      $result->setUrl($new_url);
     }
 
     // Restore page parameter again. See https://www.drupal.org/node/2726455.
diff --git a/tests/facets_query_processor/src/Plugin/facets/url_processor/DummyQuery.php b/tests/facets_query_processor/src/Plugin/facets/url_processor/DummyQuery.php
index 8eead55..f757363 100644
--- a/tests/facets_query_processor/src/Plugin/facets/url_processor/DummyQuery.php
+++ b/tests/facets_query_processor/src/Plugin/facets/url_processor/DummyQuery.php
@@ -82,11 +82,8 @@ class DummyQuery extends UrlProcessorPluginBase {
       }
 
       $result_get_params->set($this->filterKey, $filter_params);
-      $request = $this->request;
-      if ($facet->getFacetSource()->getPath()) {
-        $request = Request::create($facet->getFacetSource()->getPath());
-      }
-      $url = Url::createFromRequest($request);
+
+      $url = $facet->getFacetSource()->getPath();
       $url->setOption('query', $result_get_params->all());
 
       $result->setUrl($url);
diff --git a/tests/src/Functional/BlockTestTrait.php b/tests/src/Functional/BlockTestTrait.php
index aedc178..ffb9c22 100644
--- a/tests/src/Functional/BlockTestTrait.php
+++ b/tests/src/Functional/BlockTestTrait.php
@@ -33,12 +33,12 @@ trait BlockTestTrait {
 
     $this->drupalGet($facet_add_page);
 
-    $facet_source = "views_page:{$source}__{$display_id}";
+    $facet_source = "search_api:{$source}__{$display_id}";
     $form_values = [
       'id' => $id,
       'name' => $name,
       'facet_source_id' => $facet_source,
-      "facet_source_configs[views_page:{$source}__{$display_id}][field_identifier]" => $field,
+      "facet_source_configs[search_api:{$source}__{$display_id}][field_identifier]" => $field,
     ];
     $this->drupalPostForm(NULL, ['facet_source_id' => $facet_source], 'Configure facet source');
     $this->drupalPostForm(NULL, $form_values, 'Save');
diff --git a/tests/src/Functional/FacetSourceTest.php b/tests/src/Functional/FacetSourceTest.php
index 7db5756..bb31c28 100644
--- a/tests/src/Functional/FacetSourceTest.php
+++ b/tests/src/Functional/FacetSourceTest.php
@@ -49,7 +49,7 @@ class FacetSourceTest extends FacetsTestBase {
     $this->assertResponse(200);
 
     $this->assertUrl('admin/config/search/facets');
-    $this->assertText('Facet source views_page:search_api_test_view__block_1 has been saved.');
+    $this->assertText('Facet source search_api:search_api_test_view__page_1 has been saved.');
     $this->clickLink('Configure');
 
     // Test that saving worked filter_key has the new value.
@@ -72,7 +72,7 @@ class FacetSourceTest extends FacetsTestBase {
     $this->assertResponse(200);
 
     $this->assertUrl('admin/config/search/facets');
-    $this->assertText('Facet source views_page:search_api_test_view__block_1 has been saved.');
+    $this->assertText('Facet source search_api:search_api_test_view__page_1 has been saved.');
     $this->clickLink('Configure');
 
     // Test that saving worked and that the url processor has the new value.
diff --git a/tests/src/Functional/IntegrationTest.php b/tests/src/Functional/IntegrationTest.php
index 51554be..0dde302 100644
--- a/tests/src/Functional/IntegrationTest.php
+++ b/tests/src/Functional/IntegrationTest.php
@@ -348,13 +348,13 @@ class IntegrationTest extends FacetsTestBase {
 
     // Configure the facet source by selecting one of the Search API views.
     $this->drupalGet($facet_add_page);
-    $this->drupalPostForm(NULL, ['facet_source_id' => 'views_page:search_api_test_view__page_1'], 'Configure facet source');
+    $this->drupalPostForm(NULL, ['facet_source_id' => 'search_api:search_api_test_view__page_1'], 'Configure facet source');
 
     // Fill in all fields and make sure the 'field is required' message is no
     // longer shown.
     $facet_source_form = [
-      'facet_source_id' => 'views_page:search_api_test_view__page_1',
-      'facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]' => 'type',
+      'facet_source_id' => 'search_api:search_api_test_view__page_1',
+      'facet_source_configs[search_api:search_api_test_view__page_1][field_identifier]' => 'type',
     ];
     $this->drupalPostForm(NULL, $facet_source_form, 'Save');
 
@@ -542,8 +542,8 @@ class IntegrationTest extends FacetsTestBase {
     $this->assertResponse(200);
 
     // Check that the expected facet sources and the owl facet are shown.
-    $this->assertText('views_page:search_api_test_view__block_1');
-    $this->assertText('views_page:search_api_test_view__page_1');
+    $this->assertText('search_api:search_api_test_view__block_1');
+    $this->assertText('search_api:search_api_test_view__page_1');
     $this->assertText($name);
 
     // Delete the view on which both facet sources are based.
@@ -554,8 +554,8 @@ class IntegrationTest extends FacetsTestBase {
     // and the facet/facet source are deleted.
     $this->drupalGet('/admin/config/search/facets');
     $this->assertResponse(200);
-    $this->assertNoText('views_page:search_api_test_view__page_1');
-    $this->assertNoText('views_page:search_api_test_view__block_1');
+    $this->assertNoText('search_api:search_api_test_view__page_1');
+    $this->assertNoText('search_api:search_api_test_view__block_1');
     $this->assertNoText($name);
   }
 
@@ -582,8 +582,8 @@ class IntegrationTest extends FacetsTestBase {
     $this->assertResponse(200);
 
     // Check that the expected facet sources and the owl facet are shown.
-    $this->assertText('views_page:search_api_test_view__block_1');
-    $this->assertText('views_page:search_api_test_view__page_1');
+    $this->assertText('search_api:search_api_test_view__block_1');
+    $this->assertText('search_api:search_api_test_view__page_1');
     $this->assertText($name);
 
     // Delete the view display for the page.
@@ -595,8 +595,8 @@ class IntegrationTest extends FacetsTestBase {
     // and the facet/facet source are deleted.
     $this->drupalGet('/admin/config/search/facets');
     $this->assertResponse(200);
-    $this->assertNoText('views_page:search_api_test_view__page_1');
-    $this->assertText('views_page:search_api_test_view__block_1');
+    $this->assertNoText('search_api:search_api_test_view__page_1');
+    $this->assertText('search_api:search_api_test_view__block_1');
     $this->assertNoText($name);
   }
 
@@ -731,8 +731,8 @@ class IntegrationTest extends FacetsTestBase {
     $this->assertNoText('Field:');
 
     // Check that the expected facet sources are shown.
-    $this->assertText('views_page:search_api_test_view__block_1');
-    $this->assertText('views_page:search_api_test_view__page_1');
+    $this->assertText('search_api:search_api_test_view__block_1');
+    $this->assertText('search_api:search_api_test_view__page_1');
   }
 
   /**
@@ -767,7 +767,7 @@ class IntegrationTest extends FacetsTestBase {
 
     // Configure the facet source by selecting one of the Search API views.
     $this->drupalGet($facet_add_page);
-    $this->drupalPostForm(NULL, ['facet_source_id' => 'views_page:search_api_test_view__page_1'], 'Configure facet source');
+    $this->drupalPostForm(NULL, ['facet_source_id' => 'search_api:search_api_test_view__page_1'], 'Configure facet source');
 
     // The field is still required.
     $this->drupalPostForm(NULL, $form_values, 'Save');
@@ -776,8 +776,8 @@ class IntegrationTest extends FacetsTestBase {
     // Fill in all fields and make sure the 'field is required' message is no
     // longer shown.
     $facet_source_form = [
-      'facet_source_id' => 'views_page:search_api_test_view__page_1',
-      'facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]' => $facet_type,
+      'facet_source_id' => 'search_api:search_api_test_view__page_1',
+      'facet_source_configs[search_api:search_api_test_view__page_1][field_identifier]' => $facet_type,
     ];
     $this->drupalPostForm(NULL, $form_values + $facet_source_form, 'Save');
     $this->assertNoText('field is required.');
@@ -804,10 +804,10 @@ class IntegrationTest extends FacetsTestBase {
     $form_values = [
       'name' => $facet_name,
       'id' => $facet_id,
-      'facet_source_id' => 'views_page:search_api_test_view__page_1',
+      'facet_source_id' => 'search_api:search_api_test_view__page_1',
     ];
 
-    $facet_source_configs['facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]'] = $facet_type;
+    $facet_source_configs['facet_source_configs[search_api:search_api_test_view__page_1][field_identifier]'] = $facet_type;
 
     // Try to submit a facet with a duplicate machine name after form rebuilding
     // via facet source submit.
diff --git a/tests/src/Functional/UrlIntegrationTest.php b/tests/src/Functional/UrlIntegrationTest.php
index ee64250..6c13f6f 100644
--- a/tests/src/Functional/UrlIntegrationTest.php
+++ b/tests/src/Functional/UrlIntegrationTest.php
@@ -83,7 +83,7 @@ class UrlIntegrationTest extends FacetsTestBase {
 
     // Go to the only enabled facet source's config and change the filter key.
     $this->drupalGet('admin/config/search/facets');
-    $this->clickLink('Configure', 1);
+    $this->clickLink('Configure', 0);
 
     $edit = [
       'filter_key' => 'y',
@@ -106,7 +106,7 @@ class UrlIntegrationTest extends FacetsTestBase {
     // Go to the only enabled facet source's config and change the url
     // processor.
     $this->drupalGet('admin/config/search/facets');
-    $this->clickLink('Configure', 1);
+    $this->clickLink('Configure', 0);
 
     $edit = [
       'filter_key' => 'y',
diff --git a/tests/src/FunctionalJavascript/WidgetJSTest.php b/tests/src/FunctionalJavascript/WidgetJSTest.php
index 752af7b..0cb6fc2 100644
--- a/tests/src/FunctionalJavascript/WidgetJSTest.php
+++ b/tests/src/FunctionalJavascript/WidgetJSTest.php
@@ -52,10 +52,10 @@ class WidgetJSTest extends JavascriptTestBase {
 
     // Select one of the options from the facet source dropdown and wait for the
     // result to show.
-    $page->selectFieldOption('edit-facet-source-id', 'views_page:search_api_test_view__page_1');
+    $page->selectFieldOption('edit-facet-source-id', 'search_api:search_api_test_view__page_1');
     $this->getSession()->wait(6000, "jQuery('.facet-source-field-wrapper').length > 0");
 
-    $page->selectFieldOption('facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]', 'type');
+    $page->selectFieldOption('facet_source_configs[search_api:search_api_test_view__page_1][field_identifier]', 'type');
 
     // Check that after choosing the field, the name is already filled in.
     $field_value = $this->getSession()->getPage()->findField('edit-name')->getValue();
@@ -74,7 +74,7 @@ class WidgetJSTest extends JavascriptTestBase {
       'id' => $id,
       'name' => strtoupper($id),
       'url_alias' => $id,
-      'facet_source_id' => 'views_page:search_api_test_view__page_1',
+      'facet_source_id' => 'search_api:search_api_test_view__page_1',
       'field_identifier' => 'type',
       'empty_behavior' => ['behavior' => 'none'],
       'widget' => [
@@ -133,7 +133,7 @@ class WidgetJSTest extends JavascriptTestBase {
       'id' => $id,
       'name' => strtoupper($id),
       'url_alias' => $id,
-      'facet_source_id' => 'views_page:search_api_test_view__page_1',
+      'facet_source_id' => 'search_api:search_api_test_view__page_1',
       'field_identifier' => 'type',
       'empty_behavior' => ['behavior' => 'none'],
       'widget' => [
diff --git a/tests/src/Kernel/Entity/FacetFacetSourceTest.php b/tests/src/Kernel/Entity/FacetFacetSourceTest.php
index b3ab6bd..45987cb 100644
--- a/tests/src/Kernel/Entity/FacetFacetSourceTest.php
+++ b/tests/src/Kernel/Entity/FacetFacetSourceTest.php
@@ -3,7 +3,9 @@
 namespace Drupal\Tests\facets\Kernel\Entity;
 
 use Drupal\facets\Entity\Facet;
-use Drupal\KernelTests\KernelTestBase;
+use Drupal\facets\FacetSourceInterface;
+use Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay;
+use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
 
 /**
  * Class FacetFacetSourceTest.
@@ -13,24 +15,19 @@ use Drupal\KernelTests\KernelTestBase;
  * @group facets
  * @coversDefaultClass \Drupal\facets\Entity\Facet
  */
-class FacetFacetSourceTest extends KernelTestBase {
+class FacetFacetSourceTest extends EntityKernelTestBase {
 
   /**
    * {@inheritdoc}
    */
   public static $modules = [
     'facets',
-    'field',
+    'facets_search_api_dependency',
     'search_api',
     'search_api_db',
     'search_api_test_db',
     'search_api_test_example_content',
     'search_api_test_views',
-    'search_api_test',
-    'user',
-    'system',
-    'entity_test',
-    'text',
     'views',
     'rest',
     'serialization',
@@ -75,22 +72,28 @@ class FacetFacetSourceTest extends KernelTestBase {
     $entity = new Facet([], 'facets_facet');
     $this->assertNull($entity->getFacetSourceId());
 
-    $display_name = 'views_page:search_api_test_view__page_1';
-    $display_id = 'views_page__search_api_test_view__page_1';
+    // Check that the facet source is in the list of search api displays.
     $displays = $this->container
       ->get('plugin.manager.search_api.display')
       ->getDefinitions();
-    $this->assertArrayHasKey($display_name, $displays);
+    $this->assertTrue(isset($displays['views_page:search_api_test_view__page_1']));
+    $this->assertArrayHasKey('views_page:search_api_test_view__page_1', $displays);
 
+    // Check that has transformed into a facet source as expected.
+    $facet_sources = $this->container
+      ->get('plugin.manager.facets.facet_source')
+      ->getDefinitions();
+    $this->assertArrayHasKey('search_api:search_api_test_view__page_1', $facet_sources);
+
+    // Check the behavior of the facet sources.
+    $display_name = 'search_api:search_api_test_view__page_1';
     $entity->setFacetSourceId($display_name);
     $this->assertEquals($display_name, $entity->getFacetSourceId());
-    $this->assertInstanceOf('\Drupal\facets\FacetSource\SearchApiFacetSourceInterface', $entity->getFacetSources()[$display_name]);
-    $this->assertInstanceOf('\Drupal\facets\FacetSource\SearchApiFacetSourceInterface', $entity->getFacetSource());
-    $this->assertInstanceOf('\Drupal\facets\FacetSourceInterface', $entity->getFacetSourceConfig());
+    $this->assertInstanceOf(SearchApiDisplay::class, $entity->getFacetSources()[$display_name]);
+    $this->assertInstanceOf(SearchApiDisplay::class, $entity->getFacetSource());
+    $this->assertInstanceOf(FacetSourceInterface::class , $entity->getFacetSourceConfig());
     $this->assertEquals($display_name, $entity->getFacetSourceConfig()->getName());
-    $this->assertEquals($display_id, $entity->getFacetSourceConfig()->id());
     $this->assertEquals('f', $entity->getFacetSourceConfig()->getFilterKey());
-
   }
 
   /**
@@ -101,7 +104,7 @@ class FacetFacetSourceTest extends KernelTestBase {
   public function testInvalidQueryType() {
     $entity = new Facet([], 'facets_facet');
     $entity->setWidget('links');
-    $entity->setFacetSourceId('views_page:search_api_test_view__page_1');
+    $entity->setFacetSourceId('search_api:search_api_test_view__page_1');
 
     $this->setExpectedException('Drupal\facets\Exception\InvalidQueryTypeException');
     $entity->getQueryType();
diff --git a/tests/src/Unit/FacetSource/FacetSourcePluginManagerTest.php b/tests/src/Unit/FacetSource/FacetSourcePluginManagerTest.php
index ad648d2..99de6ab 100644
--- a/tests/src/Unit/FacetSource/FacetSourcePluginManagerTest.php
+++ b/tests/src/Unit/FacetSource/FacetSourcePluginManagerTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\facets\Unit\FacetSource;
 
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Component\Plugin\Exception\PluginException;
 use Drupal\Component\Plugin\Factory\DefaultFactory;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -83,22 +84,56 @@ class FacetSourcePluginManagerTest extends UnitTestCase {
   public function testConstruct() {
     $namespaces = new ArrayObject();
     $sut = new FacetSourcePluginManager($namespaces, $this->cache, $this->moduleHandler);
-    $this->assertInstanceOf('\Drupal\facets\FacetSource\FacetSourcePluginManager', $sut);
+    $this->assertInstanceOf(FacetSourcePluginManager::class, $sut);
   }
 
   /**
    * Tests plugin manager's getDefinitions method.
    */
   public function testGetDefinitions() {
-    $definitions = array(
-      'foo' => array(
-        'label' => $this->randomMachineName(),
-      ),
-    );
+    $definitions = [
+      'foo' => [
+        'id' => 'foo_bar',
+        'label' => 'Foo bar',
+        'description' => 'test',
+        'display_id' => 'foo',
+      ],
+    ];
     $this->discovery->expects($this->once())
       ->method('getDefinitions')
       ->willReturn($definitions);
     $this->assertSame($definitions, $this->sut->getDefinitions());
   }
 
+  /**
+   * Tests plugin manager definitions.
+   *
+   * @dataProvider invalidDefinitions
+   */
+  public function testInvalidDefinitions($invalid_definition) {
+    $definitions = ['foo' => [$invalid_definition]];
+
+    $this->discovery->expects($this->once())
+      ->method('getDefinitions')
+      ->willReturn($definitions);
+
+    $this->setExpectedException(PluginException::class);
+    $this->sut->getDefinitions();
+  }
+
+  /**
+   * Provides invalid definitions.
+   *
+   * @return array
+   *   An invalid data provider.
+   */
+  public function invalidDefinitions() {
+    return [
+      'only id' => ['id' => 'owl'],
+      'only display_id' => ['display_id' => 'search_api:owl'],
+      'only label' => ['label' => 'Owl'],
+      'no label' => ['id' => 'owl', 'display_id' => 'Owl']
+    ];
+  }
+
 }
diff --git a/tests/src/Unit/Plugin/processor/TranslateEntityProcessorTest.php b/tests/src/Unit/Plugin/processor/TranslateEntityProcessorTest.php
index af6a55a..5de7d64 100644
--- a/tests/src/Unit/Plugin/processor/TranslateEntityProcessorTest.php
+++ b/tests/src/Unit/Plugin/processor/TranslateEntityProcessorTest.php
@@ -7,7 +7,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\facets\Entity\Facet;
-use Drupal\facets\FacetSource\SearchApiFacetSourceInterface;
+use Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay;
 use Drupal\facets\Plugin\facets\processor\TranslateEntityProcessor;
 use Drupal\facets\Result\Result;
 use Drupal\field\FieldStorageConfigInterface;
@@ -66,7 +66,9 @@ class TranslateEntityProcessorTest extends UnitTestCase {
     $index->expects($this->any())
       ->method('getField')
       ->willReturn($field);
-    $facet_source = $this->getMock(SearchApiFacetSourceInterface::class);
+    $facet_source = $this->getMockBuilder(SearchApiDisplay::class)
+      ->disableOriginalConstructor()
+      ->getMock();
     $facet_source->expects($this->any())
       ->method('getIndex')
       ->willReturn($index);
diff --git a/tests/src/Unit/Plugin/url_processor/QueryStringTest.php b/tests/src/Unit/Plugin/url_processor/QueryStringTest.php
index 089dae1..0798c8c 100644
--- a/tests/src/Unit/Plugin/url_processor/QueryStringTest.php
+++ b/tests/src/Unit/Plugin/url_processor/QueryStringTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\facets\Unit\Plugin\url_processor;
 
+use Drupal\Core\Url;
 use Drupal\facets\Entity\Facet;
 use Drupal\facets\Entity\FacetSource;
 use Drupal\facets\Plugin\facets\url_processor\QueryString;
@@ -238,11 +239,13 @@ class QueryStringTest extends UnitTestCase {
         ]
       );
 
+    $validator = $this->getMock('Drupal\Core\Path\PathValidatorInterface');
+
     $fsi = $this->getMockBuilder('\Drupal\facets\FacetSource\FacetSourcePluginInterface')
       ->disableOriginalConstructor()
       ->getMock();
     $fsi->method('getPath')
-      ->willReturn('search/test');
+      ->willReturn(new Url('test'));
 
     $manager = $this->getMockBuilder('\Drupal\facets\FacetSource\FacetSourcePluginManager')
       ->disableOriginalConstructor()
@@ -266,6 +269,7 @@ class QueryStringTest extends UnitTestCase {
     $container->set('plugin.manager.facets.facet_source', $manager);
     $container->set('entity_type.manager', $em);
     $container->set('entity.manager', $em);
+    $container->set('path.validator', $validator);
     \Drupal::setContainer($container);
   }
 
