diff --git a/src/Hook/SubmitIndicatorHooks.php b/src/Hook/SubmitIndicatorHooks.php
new file mode 100644
index 0000000..ce195de
--- /dev/null
+++ b/src/Hook/SubmitIndicatorHooks.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\submit_indicator\Hook;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for submit_indicator.
+ */
+class SubmitIndicatorHooks {
+
+  /**
+   * Implements hook_form_alter().
+   *
+   * Attaches the submit indicator library to forms and processes
+   * #submit_indicator properties on forms and buttons.
+   */
+  #[Hook('form_alter')]
+  public static function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void {
+    $config = \Drupal::config('submit_indicator.settings');
+    // Always add cache tag so forms re-evaluate when config changes.
+    // This is needed even for excluded forms - if config changes to include
+    // them, the cached exclusion decision needs to be invalidated.
+    $form['#cache']['tags'][] = 'config:submit_indicator.settings';
+    // Build global defaults from config.
+    $global_defaults = [
+      'type' => $config->get('default_type') ?? 'spinner',
+      'color' => $config->get('spinner_color') ?? '#ffffff',
+      'message' => $config->get('throbber_message') ?? 'Please wait...',
+      'disableButton' => (bool) ($config->get('disable_button') ?? TRUE),
+      'timeout' => ($config->get('timeout') ?? 30) * 1000,
+      'excludeClass' => $config->get('exclude_class') ?? 'no-submit-indicator',
+    ];
+    // Determine if form should have indicator.
+    $form_indicator = _submit_indicator_resolve_form_settings($form, $form_id, $config, $global_defaults);
+    // If FALSE, skip this form entirely.
+    if ($form_indicator === FALSE) {
+      return;
+    }
+    // Skip AJAX forms - they have their own progress indicators.
+    if (_submit_indicator_is_ajax_form($form)) {
+      return;
+    }
+    // Attach the library.
+    $form['#attached']['library'][] = 'submit_indicator/submit-indicator';
+    // Pass global defaults to JS.
+    $form['#attached']['drupalSettings']['submitIndicator']['defaults'] = $form_indicator;
+    // Find buttons and process their #submit_indicator properties.
+    _submit_indicator_process_buttons($form, $form_indicator);
+  }
+
+}
diff --git a/submit_indicator.info.yml b/submit_indicator.info.yml
index 290acce..eafc441 100644
--- a/submit_indicator.info.yml
+++ b/submit_indicator.info.yml
@@ -2,4 +2,4 @@ name: Submit Indicator
 type: module
 description: Shows a loading indicator on form submit buttons during submission.
 package: Custom
-core_version_requirement: ^10 || ^11
+core_version_requirement: ^10.1 || ^11 || ^12
diff --git a/submit_indicator.module b/submit_indicator.module
index 3c4ef46..8fd641f 100644
--- a/submit_indicator.module
+++ b/submit_indicator.module
@@ -5,6 +5,8 @@
  * Shows loading indicators on form submit buttons.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\submit_indicator\Hook\SubmitIndicatorHooks;
 use Drupal\Core\Form\FormStateInterface;
 
 /**
@@ -13,45 +15,9 @@ use Drupal\Core\Form\FormStateInterface;
  * Attaches the submit indicator library to forms and processes
  * #submit_indicator properties on forms and buttons.
  */
+#[LegacyHook]
 function submit_indicator_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
-  $config = \Drupal::config('submit_indicator.settings');
-
-  // Always add cache tag so forms re-evaluate when config changes.
-  // This is needed even for excluded forms - if config changes to include
-  // them, the cached exclusion decision needs to be invalidated.
-  $form['#cache']['tags'][] = 'config:submit_indicator.settings';
-
-  // Build global defaults from config.
-  $global_defaults = [
-    'type' => $config->get('default_type') ?? 'spinner',
-    'color' => $config->get('spinner_color') ?? '#ffffff',
-    'message' => $config->get('throbber_message') ?? 'Please wait...',
-    'disableButton' => (bool) ($config->get('disable_button') ?? TRUE),
-    'timeout' => (($config->get('timeout') ?? 30) * 1000),
-    'excludeClass' => $config->get('exclude_class') ?? 'no-submit-indicator',
-  ];
-
-  // Determine if form should have indicator.
-  $form_indicator = _submit_indicator_resolve_form_settings($form, $form_id, $config, $global_defaults);
-
-  // If FALSE, skip this form entirely.
-  if ($form_indicator === FALSE) {
-    return;
-  }
-
-  // Skip AJAX forms - they have their own progress indicators.
-  if (_submit_indicator_is_ajax_form($form)) {
-    return;
-  }
-
-  // Attach the library.
-  $form['#attached']['library'][] = 'submit_indicator/submit-indicator';
-
-  // Pass global defaults to JS.
-  $form['#attached']['drupalSettings']['submitIndicator']['defaults'] = $form_indicator;
-
-  // Find buttons and process their #submit_indicator properties.
-  _submit_indicator_process_buttons($form, $form_indicator);
+  \Drupal::service(SubmitIndicatorHooks::class)->formAlter($form, $form_state, $form_id);
 }
 
 /**
diff --git a/submit_indicator.services.yml b/submit_indicator.services.yml
new file mode 100644
index 0000000..785a7a4
--- /dev/null
+++ b/submit_indicator.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\submit_indicator\Hook\SubmitIndicatorHooks:
+    class: Drupal\submit_indicator\Hook\SubmitIndicatorHooks
+    autowire: true
diff --git a/tests/modules/submit_indicator_test/submit_indicator_test.info.yml b/tests/modules/submit_indicator_test/submit_indicator_test.info.yml
index 49ee02f..61dc996 100644
--- a/tests/modules/submit_indicator_test/submit_indicator_test.info.yml
+++ b/tests/modules/submit_indicator_test/submit_indicator_test.info.yml
@@ -2,7 +2,7 @@ name: 'Submit Indicator Test'
 type: module
 description: 'Test module for Submit Indicator.'
 package: Testing
-core_version_requirement: ^10.5 || ^11
+core_version_requirement: ^10.5 || ^11 || ^12
 dependencies:
   - submit_indicator:submit_indicator
 hidden: true
diff --git a/tests/src/FunctionalJavascript/SubmitIndicatorTest.php b/tests/src/FunctionalJavascript/SubmitIndicatorTest.php
index 8511305..fa78949 100644
--- a/tests/src/FunctionalJavascript/SubmitIndicatorTest.php
+++ b/tests/src/FunctionalJavascript/SubmitIndicatorTest.php
@@ -4,11 +4,16 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\submit_indicator\FunctionalJavascript;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
+
 /**
  * Tests Submit Indicator JavaScript functionality.
  *
  * @group submit_indicator
  */
+#[Group('submit_indicator')]
+#[RunTestsInSeparateProcesses]
 class SubmitIndicatorTest extends SubmitIndicatorTestBase {
 
   /**
