diff --git a/shortcode.module b/shortcode.module
index fac676b..045c249 100644
--- a/shortcode.module
+++ b/shortcode.module
@@ -1,5 +1,12 @@
 <?php
 
+/**
+ * @file
+ */
+
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\shortcode\Hook\ShortcodeHooks;
+
 /**
  * @file
  * Module provides ShortCodes filter framework and API (like WP ShortCodes)
@@ -8,11 +15,7 @@
 /**
  * Implements hook_migration_plugins_alter().
  */
+#[LegacyHook]
 function shortcode_migration_plugins_alter(array &$migrations) {
-  if (isset($migrations['d7_filter_format'])) {
-    $migration = &$migrations['d7_filter_format'];
-
-    // Add 7.x-2.x to 2.0.x filter ID mapping.
-    $migration['process']['filters']['process']['id']['map']['shortcode_text_corrector'] = 'shortcode_corrector';
-  }
+  \Drupal::service(ShortcodeHooks::class)->migrationPluginsAlter($migrations);
 }
diff --git a/shortcode.services.yml b/shortcode.services.yml
index f138cc1..077192a 100644
--- a/shortcode.services.yml
+++ b/shortcode.services.yml
@@ -5,3 +5,7 @@ services:
   shortcode:
     class: Drupal\shortcode\ShortcodeService
     arguments: ['@plugin.manager.shortcode']
+
+  Drupal\shortcode\Hook\ShortcodeHooks:
+    class: Drupal\shortcode\Hook\ShortcodeHooks
+    autowire: true
diff --git a/shortcode_basic_tags/shortcode_basic_tags.module b/shortcode_basic_tags/shortcode_basic_tags.module
index 3457aeb..4591be1 100644
--- a/shortcode_basic_tags/shortcode_basic_tags.module
+++ b/shortcode_basic_tags/shortcode_basic_tags.module
@@ -1,5 +1,12 @@
 <?php
 
+/**
+ * @file
+ */
+
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\shortcode_basic_tags\Hook\ShortcodeBasicTagsHooks;
+
 /**
  * @file
  * Register hooks for ShortCodes that use Twig templates.
@@ -8,35 +15,7 @@
 /**
  * Implements hook_theme().
  */
+#[LegacyHook]
 function shortcode_basic_tags_theme() {
-  return [
-    // Quote.
-    'shortcode_quote' => [
-      'variables' => ['author' => NULL, 'class' => NULL, 'text' => ''],
-    ],
-    // Image.
-    'shortcode_img' => [
-      'variables' => ['src' => NULL, 'alt' => NULL, 'class' => ''],
-    ],
-    // Button.
-    'shortcode_button' => [
-      'variables' => ['url' => NULL, 'attributes' => [], 'text' => ''],
-    ],
-    // Dropcap.
-    'shortcode_dropcap' => [
-      'variables' => ['class' => NULL, 'text' => ''],
-    ],
-    // Item.
-    'shortcode_item' => [
-      'variables' => ['type' => 'div', 'attributes' => [], 'text' => ''],
-    ],
-    // Clear.
-    'shortcode_clear' => [
-      'variables' => ['type' => 'div', 'attributes' => [], 'text' => ''],
-    ],
-    // Link.
-    'shortcode_link' => [
-      'variables' => ['url' => NULL, 'attributes' => [], 'text' => ''],
-    ],
-  ];
+  return \Drupal::service(ShortcodeBasicTagsHooks::class)->theme();
 }
diff --git a/shortcode_basic_tags/shortcode_basic_tags.services.yml b/shortcode_basic_tags/shortcode_basic_tags.services.yml
new file mode 100644
index 0000000..7b6e9cf
--- /dev/null
+++ b/shortcode_basic_tags/shortcode_basic_tags.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\shortcode_basic_tags\Hook\ShortcodeBasicTagsHooks:
+    class: Drupal\shortcode_basic_tags\Hook\ShortcodeBasicTagsHooks
+    autowire: true
diff --git a/shortcode_basic_tags/src/Hook/ShortcodeBasicTagsHooks.php b/shortcode_basic_tags/src/Hook/ShortcodeBasicTagsHooks.php
new file mode 100644
index 0000000..75a3202
--- /dev/null
+++ b/shortcode_basic_tags/src/Hook/ShortcodeBasicTagsHooks.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\shortcode_basic_tags\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for shortcode_basic_tags.
+ */
+class ShortcodeBasicTagsHooks {
+  /**
+   * @file
+   * Register hooks for ShortCodes that use Twig templates.
+   */
+
+  /**
+   * Implements hook_theme().
+   */
+  #[Hook('theme')]
+  public function theme() {
+    return [
+          // Quote.
+      'shortcode_quote' => [
+        'variables' => [
+          'author' => NULL,
+          'class' => NULL,
+          'text' => '',
+        ],
+      ],
+          // Image.
+      'shortcode_img' => [
+        'variables' => [
+          'src' => NULL,
+          'alt' => NULL,
+          'class' => '',
+        ],
+      ],
+          // Button.
+      'shortcode_button' => [
+        'variables' => [
+          'url' => NULL,
+          'attributes' => [],
+          'text' => '',
+        ],
+      ],
+          // Dropcap.
+      'shortcode_dropcap' => [
+        'variables' => [
+          'class' => NULL,
+          'text' => '',
+        ],
+      ],
+          // Item.
+      'shortcode_item' => [
+        'variables' => [
+          'type' => 'div',
+          'attributes' => [],
+          'text' => '',
+        ],
+      ],
+          // Clear.
+      'shortcode_clear' => [
+        'variables' => [
+          'type' => 'div',
+          'attributes' => [],
+          'text' => '',
+        ],
+      ],
+          // Link.
+      'shortcode_link' => [
+        'variables' => [
+          'url' => NULL,
+          'attributes' => [],
+          'text' => '',
+        ],
+      ],
+    ];
+  }
+
+}
diff --git a/src/Hook/ShortcodeHooks.php b/src/Hook/ShortcodeHooks.php
new file mode 100644
index 0000000..e99d8a5
--- /dev/null
+++ b/src/Hook/ShortcodeHooks.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\shortcode\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for shortcode.
+ */
+class ShortcodeHooks {
+  /**
+   * @file
+   * Module provides ShortCodes filter framework and API (like WP ShortCodes)
+   */
+
+  /**
+   * Implements hook_migration_plugins_alter().
+   */
+  #[Hook('migration_plugins_alter')]
+  public function migrationPluginsAlter(array &$migrations) {
+    if (isset($migrations['d7_filter_format'])) {
+      $migration =& $migrations['d7_filter_format'];
+      // Add 7.x-2.x to 2.0.x filter ID mapping.
+      $migration['process']['filters']['process']['id']['map']['shortcode_text_corrector'] = 'shortcode_corrector';
+    }
+  }
+
+}
diff --git a/src/ShortcodeService.php b/src/ShortcodeService.php
index bdeda4c..786601c 100644
--- a/src/ShortcodeService.php
+++ b/src/ShortcodeService.php
@@ -86,7 +86,7 @@ class ShortcodeService {
    * @return array
    *   Array of shortcode plugin definitions, keyed by token, not id.
    */
-  public function getShortcodePlugins(FilterInterface $filter = NULL, bool $reset = FALSE): array {
+  public function getShortcodePlugins(?FilterInterface $filter = NULL, bool $reset = FALSE): array {
     $shortcodes = &drupal_static(__FUNCTION__);
 
     // Prime plugin cache.
@@ -212,7 +212,7 @@ class ShortcodeService {
    * @return string
    *   The processed string.
    */
-  public function process($text, string $langcode = Language::LANGCODE_NOT_SPECIFIED, FilterInterface $filter = NULL): string {
+  public function process($text, string $langcode = Language::LANGCODE_NOT_SPECIFIED, ?FilterInterface $filter = NULL): string {
     $shortcodes = $this->getShortcodePlugins($filter);
 
     // Processing recursively, now embedding tags within other tags is
@@ -357,7 +357,7 @@ class ShortcodeService {
    * @return string
    *   The processed string.
    */
-  public function postprocessText($text, string $langcode, FilterInterface $filter = NULL): string {
+  public function postprocessText($text, string $langcode, ?FilterInterface $filter = NULL): string {
 
     // preg_match_all('/<p>s.*<!--.*-->.*<div/isU', $text, $r);
     // dpm($r, '$r');
diff --git a/tests/src/Functional/ShortcodeTest.php b/tests/src/Functional/ShortcodeTest.php
index 4479d31..7b88fe9 100644
--- a/tests/src/Functional/ShortcodeTest.php
+++ b/tests/src/Functional/ShortcodeTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\shortcode\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Core\Url;
 use Drupal\filter\Entity\FilterFormat;
 use Drupal\node\NodeInterface;
@@ -12,6 +14,8 @@ use Drupal\Tests\BrowserTestBase;
  *
  * @group shortcode
  */
+#[Group('shortcode')]
+#[RunTestsInSeparateProcesses]
 class ShortcodeTest extends BrowserTestBase {
 
   /**
