diff --git a/modules/recurring_events_registration/recurring_events_registration.links.menu.yml b/modules/recurring_events_registration/recurring_events_registration.links.menu.yml
index 7881024c5b7a99e2f11b4a1578a23dcb6f728d76..751363f52668833f0127518e2dd6254238d16924 100644
--- a/modules/recurring_events_registration/recurring_events_registration.links.menu.yml
+++ b/modules/recurring_events_registration/recurring_events_registration.links.menu.yml
@@ -21,3 +21,11 @@ entity.registrant_type.collection:
   route_name: entity.registrant_type.collection
   parent: events.admin.overview
   weight: 13
+
+  # Orphaned Event Registrants
+recurring_events_registration.orphaned_registrants:
+  title: 'Orphaned Event Registrants'
+  description: 'Delete orphaned event registrants'
+  route_name: recurring_events_registration.orphaned_registrants
+  parent: events.admin.overview
+  weight: 101
diff --git a/modules/recurring_events_registration/recurring_events_registration.module b/modules/recurring_events_registration/recurring_events_registration.module
index 98b582f66d0f68b3ffe33b90500ea2730836f555..147f7b012d0ab0a09f41f301c8b67ac0b66313b2 100644
--- a/modules/recurring_events_registration/recurring_events_registration.module
+++ b/modules/recurring_events_registration/recurring_events_registration.module
@@ -284,20 +284,30 @@ function recurring_events_registration_recurring_events_pre_delete_instance(Even
  * Implements hook_recurring_events_pre_delete_instances().
  */
 function recurring_events_registration_recurring_events_pre_delete_instances(EventSeries $event_series) {
+  /** @var \Drupal\recurring_events_registration\RegistrationCreationService $registration_creation_service */
   $registration_creation_service = \Drupal::service('recurring_events_registration.creation_service');
   $registration_creation_service->setEventSeries($event_series);
 
-  // Get all the registrants who have registered for any event in this series.
-  $registrants = $registration_creation_service->retrieveAllSeriesRegisteredParties(TRUE);
+  // Get the registrants registered for a future event and email them about the
+  // series deletion.
+  $future_registrants = $registration_creation_service->retrieveAllSeriesRegisteredParties(TRUE);
+  if (!empty($future_registrants)) {
+    $key = 'series_deletion_notification';
+
+    // Send an email to the future registrants.
+    foreach ($future_registrants as $registrant) {
+      recurring_events_registration_send_notification($key, $registrant);
+    }
+  }
+
+  // Now get all the registrants for this series so we can remove them.
+  $registrants = $registration_creation_service->retrieveAllSeriesRegisteredParties();
   if (empty($registrants)) {
     return;
   }
 
-  $key = 'series_deletion_notification';
-
   // Send an email to all registrants.
   foreach ($registrants as $registrant) {
-    recurring_events_registration_send_notification($key, $registrant);
     $registrant->delete();
   }
 }
diff --git a/modules/recurring_events_registration/recurring_events_registration.routing.yml b/modules/recurring_events_registration/recurring_events_registration.routing.yml
index c80f322d6c7ea2c8e06abc8e195ebeae9c07b097..0e6f352901970daf407140ced074a188eebaad0a 100644
--- a/modules/recurring_events_registration/recurring_events_registration.routing.yml
+++ b/modules/recurring_events_registration/recurring_events_registration.routing.yml
@@ -191,3 +191,12 @@ entity.registrant_type.collection:
     _permission: 'administer registrant types'
   options:
     _admin_route: TRUE
+
+# Orphaned registrant cleanup.
+recurring_events_registration.orphaned_registrants:
+  path: '/admin/structure/events/orphaned-registrants'
+  defaults:
+    _form: '\Drupal\recurring_events_registration\Form\OrphanedEventRegistrantsForm'
+    _title: 'Orphaned Event Registrants'
+  requirements:
+    _permission: 'administer orphaned events entities'
diff --git a/modules/recurring_events_registration/src/Entity/Registrant.php b/modules/recurring_events_registration/src/Entity/Registrant.php
index f3c67a7e02392c1e5dbceb25c0b936605f9fc7ad..ac8fd789af119d502b9681ab50d7fec6b9ae4c29 100644
--- a/modules/recurring_events_registration/src/Entity/Registrant.php
+++ b/modules/recurring_events_registration/src/Entity/Registrant.php
@@ -377,7 +377,7 @@ class Registrant extends EditorialContentEntityBase implements RegistrantInterfa
    */
   protected function urlRouteParameters($rel) {
     $uri_route_parameters = parent::urlRouteParameters($rel);
-    $uri_route_parameters['eventinstance'] = $this->getEventInstance()->id();
+    $uri_route_parameters['eventinstance'] = $this->getEventInstance() ? $this->getEventInstance()->id() : 0;
     $uri_route_parameters['registrant'] = $this->id();
     if ($rel == 'anon-edit-form' || $rel == 'anon-delete-form') {
       $uri_route_parameters['uuid'] = $this->uuid->value;
diff --git a/modules/recurring_events_registration/src/Form/OrphanedEventRegistrantsForm.php b/modules/recurring_events_registration/src/Form/OrphanedEventRegistrantsForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..b89886b5fd764841dd554deaf480ad2340d5dac0
--- /dev/null
+++ b/modules/recurring_events_registration/src/Form/OrphanedEventRegistrantsForm.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace Drupal\recurring_events_registration\Form;
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandler;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\recurring_events_registration\Entity\RegistrantInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a form for handling orphaned registrants.
+ *
+ * @ingroup recurring_events
+ */
+class OrphanedEventRegistrantsForm extends FormBase {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandler
+   */
+  protected $moduleHandler;
+
+  /**
+   * Returns a unique string identifying the form.
+   *
+   * @return string
+   *   The unique string identifying the form.
+   */
+  public function getFormId() {
+    return 'recurring_events_registration_orphaned_registrants';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('database'),
+      $container->get('entity_type.manager'),
+      $container->get('module_handler')
+    );
+  }
+
+  /**
+   * Construct an OrphanedEventInstanceForm.
+   *
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager service.
+   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
+   *   The module handler service.
+   */
+  public function __construct(Connection $database, EntityTypeManagerInterface $entity_type_manager, ModuleHandler $module_handler) {
+    $this->database = $database;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * Form submission handler.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   An associative array containing the current state of the form.
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $registrants = $this->getOrphanedRegistrants();
+    $count = count($registrants);
+    if (!empty($registrants)) {
+      foreach ($registrants as $registrant) {
+        $registrant->delete();
+      }
+    }
+    $this->messenger()->addMessage($this->t('Successfully deleted @count registrants(s).', [
+      '@count' => $count,
+    ]));
+  }
+
+  /**
+   * Define the form used for EventInstance settings.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   An associative array containing the current state of the form.
+   *
+   * @return array
+   *   Form definition array.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $registrants = $this->getOrphanedRegistrants();
+
+    $rows = [];
+    $header = ['Registrant ID', 'Series ID', 'Label', 'Actions'];
+    $form['table'] = [
+      '#type' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#empty' => $this->t('No orphaned registrants found.'),
+    ];
+
+    if (!empty($registrants)) {
+      foreach ($registrants as $registrant) {
+        $rows[] = [
+          $registrant->id(),
+          $registrant->get('eventseries_id')->first()->target_id ?? $this->t('N/A'),
+          $registrant->label(),
+          new FormattableMarkup('@view_link | @delete_link', [
+            '@view_link' => $registrant->toLink('View')->toString(),
+            '@delete_link' => $registrant->toLink('Delete', 'delete-form')->toString(),
+          ]),
+        ];
+      }
+
+      $form['table']['#rows'] = $rows;
+
+      $form['submit'] = [
+        '#type' => 'submit',
+        '#value' => $this->t('Delete Orphaned Registrants'),
+      ];
+    }
+
+    return $form;
+  }
+
+  /**
+   * Get all the orphaned registrants.
+   *
+   * @return RegistrantInterface[]|array
+   *   An array of event instance entities, or an empty array.
+   */
+  protected function getOrphanedRegistrants() {
+    $query = $this->database
+      ->select('registrant', 'r')
+      ->fields('r', ['id']);
+    $query->leftJoin('eventseries', 'es', 'r.eventseries_id = es.id');
+    $query->leftJoin('eventinstance', 'ei', 'r.eventinstance_id = ei.id');
+    $or_group = $query->orConditionGroup()
+      ->condition('es.id', NULL, 'IS NULL')
+      ->condition('ei.id', NULL, 'IS NULL');
+    $registrants = $query->condition($or_group)
+      ->execute()
+      ->fetchCol();
+
+    if (!empty($registrants)) {
+      $registrants = $this->entityTypeManager->getStorage('registrant')->loadMultiple($registrants);
+    }
+    return $registrants;
+  }
+}
diff --git a/recurring_events.links.menu.yml b/recurring_events.links.menu.yml
index 235f7216aaeffb8c8b7450c03f8913fba4270b3b..13399974a296565d4b639e0e5a6bf795398bcd7e 100644
--- a/recurring_events.links.menu.yml
+++ b/recurring_events.links.menu.yml
@@ -75,3 +75,11 @@ entity.eventinstance_type.collection:
   route_name: entity.eventinstance_type.collection
   parent: events.admin.overview
   weight: 3
+
+# Orphaned Event Instances
+recurring_events.orphaned_instances:
+  title: 'Orphaned Event Instances'
+  description: 'Delete orphaned event instances'
+  route_name: recurring_events.orphaned_instances
+  parent: events.admin.overview
+  weight: 100
diff --git a/recurring_events.permissions.yml b/recurring_events.permissions.yml
index 76e212749bea2864d75e1bc0e1ba3dfa9496db93..eced703301071da3c27bb2d167be2b4b5f59b8ca 100644
--- a/recurring_events.permissions.yml
+++ b/recurring_events.permissions.yml
@@ -90,3 +90,9 @@ administer eventinstance types:
   title: 'Administer eventinstance types'
   description: 'Manage types of eventinstance.'
   restrict access: true
+
+# Orphaned Content Cleanup
+administer orphaned events entities:
+  title: 'Cleanup orphaned events entities'
+  description: 'Delete orphaned instances or registrants'
+  restrict access: true
diff --git a/recurring_events.routing.yml b/recurring_events.routing.yml
index 71d54467a21ac1efd364292382e6aaa636b4a3f0..a257666227ab79b4fd515f7ca48870d42b20ab79 100644
--- a/recurring_events.routing.yml
+++ b/recurring_events.routing.yml
@@ -215,6 +215,15 @@ eventinstance.settings:
   requirements:
     _permission: 'administer eventinstance entity'
 
+# Orphaned content cleanup.
+recurring_events.orphaned_instances:
+  path: '/admin/structure/events/orphaned-instances'
+  defaults:
+    _form: '\Drupal\recurring_events\Form\OrphanedEventInstanceForm'
+    _title: 'Orphaned Event Instances'
+  requirements:
+    _permission: 'administer orphaned events entities'
+
 # Event Series admin table list route.
 entity.eventseries.admin_collection:
   path: '/admin/content/events/series'
diff --git a/src/Form/OrphanedEventInstanceForm.php b/src/Form/OrphanedEventInstanceForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..70175b457aa1299ac267181340c2f7d10990af22
--- /dev/null
+++ b/src/Form/OrphanedEventInstanceForm.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace Drupal\recurring_events\Form;
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandler;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a form for handling orphaned instances.
+ *
+ * @ingroup recurring_events
+ */
+class OrphanedEventInstanceForm extends FormBase {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandler
+   */
+  protected $moduleHandler;
+
+  /**
+   * Returns a unique string identifying the form.
+   *
+   * @return string
+   *   The unique string identifying the form.
+   */
+  public function getFormId() {
+    return 'recurring_events_orphaned_instances';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('database'),
+      $container->get('entity_type.manager'),
+      $container->get('module_handler')
+    );
+  }
+
+  /**
+   * Construct an OrphanedEventInstanceForm.
+   *
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager service.
+   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
+   *   The module handler service.
+   */
+  public function __construct(Connection $database, EntityTypeManagerInterface $entity_type_manager, ModuleHandler $module_handler) {
+    $this->database = $database;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * Form submission handler.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   An associative array containing the current state of the form.
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $instances = $this->getOrphanedInstances();
+    $count = count($instances);
+    if (!empty($instances)) {
+      foreach ($instances as $instance) {
+        $this->moduleHandler->invokeAll('recurring_events_pre_delete_instance', [$instance]);
+        $instance->delete();
+      }
+    }
+    $this->messenger()->addMessage($this->t('Successfully deleted @count instance(s).', [
+      '@count' => $count,
+    ]));
+  }
+
+  /**
+   * Define the form used for EventInstance settings.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   An associative array containing the current state of the form.
+   *
+   * @return array
+   *   Form definition array.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $instances = $this->getOrphanedInstances();
+
+    $rows = [];
+    $header = ['Instance ID', 'Series ID', 'Title', 'Actions'];
+    $form['table'] = [
+      '#type' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#empty' => $this->t('No orphaned instances found.'),
+    ];
+
+    if (!empty($instances)) {
+      foreach ($instances as $instance) {
+        $rows[] = [
+          $instance->id(),
+          $instance->get('eventseries_id')->first()->target_id ?? $this->t('N/A'),
+          $instance->label() ?? $this->t('Unable to determine'),
+          new FormattableMarkup('@view_link | @delete_link', [
+            '@view_link' => $instance->toLink('View')->toString(),
+            '@delete_link' => $instance->toLink('Delete', 'delete-form')->toString(),
+          ]),
+        ];
+      }
+
+      $form['table']['#rows'] = $rows;
+
+      $form['submit'] = [
+        '#type' => 'submit',
+        '#value' => $this->t('Delete Orphaned Instances'),
+      ];
+    }
+
+    return $form;
+  }
+
+  /**
+   * Get all the orphaned instances.
+   *
+   * @return EventInstance[]|array
+   *   An array of event instance entities, or an empty array.
+   */
+  protected function getOrphanedInstances() {
+    $query = $this->database
+      ->select('eventinstance_field_data', 'efd')
+      ->fields('efd', ['id']);
+    $query->leftJoin('eventseries', 'es', 'efd.eventseries_id = es.id');
+    $instances = $query->condition('es.id', NULL, 'IS NULL')
+      ->execute()
+      ->fetchCol();
+
+    if (!empty($instances)) {
+      $instances = $this->entityTypeManager->getStorage('eventinstance')->loadMultiple($instances);
+    }
+    return $instances;
+  }
+
+}