diff --git a/entity_repeat.module b/entity_repeat.module
index 5af5c0c..7eaf1c7 100644
--- a/entity_repeat.module
+++ b/entity_repeat.module
@@ -4,7 +4,8 @@
  * @file
  * Core functionality for Entity Repeat module.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\entity_repeat\Hook\EntityRepeatHooks;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\date_recur\DateRange;
 use Drupal\date_recur\DateRecurHelperInterface;
@@ -80,7 +81,7 @@ function _entity_repeat_get_recur_field(EntityInterface $entity) {
  * @param int|null $end_date
  *   The recurring date field end date.
  */
-function _entity_repeat_create_entity(EntityInterface $entity, int $start_date, int $end_date = NULL) {
+function _entity_repeat_create_entity(EntityInterface $entity, int $start_date, ?int $end_date = NULL) {
   $format = 'Y-m-d\TH:i:s';
 
   // Clone the entity.
@@ -210,18 +211,16 @@ function _entity_repeat_batch_finished(bool $success, array $results) {
 /**
  * Implements hook_theme().
  */
+#[LegacyHook]
 function entity_repeat_theme(array $existing, string $type, string $theme, string $path): array {
-  return [
-    'entity_repeat_widget' => [
-      'render element' => 'widget',
-    ],
-  ];
+  return \Drupal::service(EntityRepeatHooks::class)->theme($existing, $type, $theme, $path);
 }
 
 /**
  * Implements hook_preprocess_HOOK().
  */
-function template_preprocess_entity_repeat_widget(array &$variables): void {
-  $variables['attributes']['class'][] = 'date-recur-modular-alpha-widget';
-  $variables['#attached']['library'][] = 'date_recur_modular/date_recur_modular_alpha_widget';
+#[LegacyHook]
+function template_preprocess_entity_repeat_widget(array &$variables): void
+{
+    \Drupal::service(EntityRepeatHooks::class)->templatePreprocessEntityRepeatWidget($variables);
 }
diff --git a/entity_repeat.services.yml b/entity_repeat.services.yml
index 2af6425..dcade51 100644
--- a/entity_repeat.services.yml
+++ b/entity_repeat.services.yml
@@ -4,3 +4,7 @@ services:
     arguments: ['@database', '@entity_field.manager', '@typed_data_manager', '@entity_type.manager']
     tags:
     - { name: 'event_subscriber' }
+
+  Drupal\entity_repeat\Hook\EntityRepeatHooks:
+    class: Drupal\entity_repeat\Hook\EntityRepeatHooks
+    autowire: true
diff --git a/modules/entity_repeat_group/entity_repeat_group.module b/modules/entity_repeat_group/entity_repeat_group.module
index 4bff247..66327a5 100644
--- a/modules/entity_repeat_group/entity_repeat_group.module
+++ b/modules/entity_repeat_group/entity_repeat_group.module
@@ -4,47 +4,18 @@
  * @file
  * Core functionality for Entity Repeat.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\entity_repeat_group\Hook\EntityRepeatGroupHooks;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Implements hook_form_alter().
  */
-function entity_repeat_group_form_alter(array &$form, FormStateInterface $form_state, string $form_id) {
-  $form_object = $form_state->getFormObject();
-  // Run only on entity forms.
-  if (!is_a($form_object, '\Drupal\Core\Entity\EntityFormInterface')) {
-    return;
-  }
-  // Get the entity being added/edited.
-  $entity = $form_state->getFormObject()->getEntity();
-  $values = ['entity_repeat_entity' => $entity];
-
-  $group = $form_state->get('group');
-  // Group is NOT in entity edit form.
-  if (empty($group)) {
-    // Get the group_content that the entity is associated with (if any).
-    $group_content = _entity_repeat_group_get_group_content($entity);
-    $values['entity_repeat_group_content'] = $group_content;
-  }
-  // Group is in entity add form.
-  else {
-    $values['entity_repeat_group'] = $group;
-  }
-
-  // Entity not in a group so bail.
-  if (empty($group) && empty($group_content)) {
-    return;
-  }
-
-  // Save group values for later.
-  $storage = $form_state->getStorage();
-  $storage += $values;
-  $form_state->setStorage($storage);
-
-  // Attach our custom submit handler.
-  $form['actions']['submit']['#submit'][] = '_entity_repeat_group_submit';
+#[LegacyHook]
+function entity_repeat_group_form_alter(array &$form, FormStateInterface $form_state, string $form_id)
+{
+    \Drupal::service(EntityRepeatGroupHooks::class)->formAlter($form, $form_state, $form_id);
 }
 
 /**
diff --git a/modules/entity_repeat_group/entity_repeat_group.services.yml b/modules/entity_repeat_group/entity_repeat_group.services.yml
new file mode 100644
index 0000000..184be3f
--- /dev/null
+++ b/modules/entity_repeat_group/entity_repeat_group.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\entity_repeat_group\Hook\EntityRepeatGroupHooks:
+    class: Drupal\entity_repeat_group\Hook\EntityRepeatGroupHooks
+    autowire: true
diff --git a/modules/entity_repeat_group/src/Hook/EntityRepeatGroupHooks.php b/modules/entity_repeat_group/src/Hook/EntityRepeatGroupHooks.php
new file mode 100644
index 0000000..aaba739
--- /dev/null
+++ b/modules/entity_repeat_group/src/Hook/EntityRepeatGroupHooks.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Drupal\entity_repeat_group\Hook;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for entity_repeat_group.
+ */
+class EntityRepeatGroupHooks
+{
+    /**
+     * Implements hook_form_alter().
+     */
+    #[Hook('form_alter')]
+    public static function formAlter(array &$form, \Drupal\Core\Form\FormStateInterface $form_state, string $form_id)
+    {
+        $form_object = $form_state->getFormObject();
+        // Run only on entity forms.
+        if (!is_a($form_object, '\Drupal\Core\Entity\EntityFormInterface')) {
+            return;
+        }
+        // Get the entity being added/edited.
+        $entity = $form_state->getFormObject()->getEntity();
+        $values = [
+            'entity_repeat_entity' => $entity,
+        ];
+        $group = $form_state->get('group');
+        // Group is NOT in entity edit form.
+        if (empty($group)) {
+            // Get the group_content that the entity is associated with (if any).
+            $group_content = _entity_repeat_group_get_group_content($entity);
+            $values['entity_repeat_group_content'] = $group_content;
+        } else {
+            $values['entity_repeat_group'] = $group;
+        }
+        // Entity not in a group so bail.
+        if (empty($group) && empty($group_content)) {
+            return;
+        }
+        // Save group values for later.
+        $storage = $form_state->getStorage();
+        $storage += $values;
+        $form_state->setStorage($storage);
+        // Attach our custom submit handler.
+        $form['actions']['submit']['#submit'][] = '_entity_repeat_group_submit';
+    }
+}
diff --git a/src/Hook/EntityRepeatHooks.php b/src/Hook/EntityRepeatHooks.php
new file mode 100644
index 0000000..9630c50
--- /dev/null
+++ b/src/Hook/EntityRepeatHooks.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\entity_repeat\Hook;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\date_recur\DateRange;
+use Drupal\date_recur\DateRecurHelperInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for entity_repeat.
+ */
+class EntityRepeatHooks
+{
+    /**
+     * Implements hook_theme().
+     */
+    #[Hook('theme')]
+    public static function theme(array $existing, string $type, string $theme, string $path): array
+    {
+        return [
+            'entity_repeat_widget' => [
+                'render element' => 'widget',
+            ],
+        ];
+    }
+    /**
+     * Implements hook_preprocess_HOOK().
+     */
+    #[Hook('preprocess_entity_repeat_widget', module: 'template')]
+    public static function templatePreprocessEntityRepeatWidget(array &$variables): void
+    {
+        $variables['attributes']['class'][] = 'date-recur-modular-alpha-widget';
+        $variables['#attached']['library'][] = 'date_recur_modular/date_recur_modular_alpha_widget';
+    }
+}
