diff --git a/pager_metadata.info.yml b/pager_metadata.info.yml
index d165533..f803eec 100644
--- a/pager_metadata.info.yml
+++ b/pager_metadata.info.yml
@@ -2,4 +2,4 @@
 name: Pager metadata
 type: module
 description: "Add SEO metadata for pages that contain a pager"
-core_version_requirement: ^8 || ^9 || ^10 || ^11
+core_version_requirement: ^10.1 || ^11 || ^12
diff --git a/pager_metadata.module b/pager_metadata.module
index 4ad696f..13c4eaf 100644
--- a/pager_metadata.module
+++ b/pager_metadata.module
@@ -5,53 +5,34 @@
  * Module implementation file.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\pager_metadata\Hook\PagerMetadataHooks;
 use Drupal\Core\Block\BlockPluginInterface;
 use Drupal\Core\Cache\CacheableMetadata;
-use Drupal\Core\Site\Settings;
 use Drupal\Core\Url;
 
 /**
  * Implements hook_page_attachments_alter().
  */
+#[LegacyHook]
 function pager_metadata_page_attachments_alter(array &$attachments) {
-  $cache = CacheableMetadata::createFromRenderArray($attachments);
-
-  if (Settings::get('pager_metadata_alter_canonical', TRUE)) {
-    foreach ($attachments['#attached']['html_head'] as &$attachment) {
-      if ($attachment[1] == 'canonical_url') {
-        $url = Url::fromUri($attachment[0]['#attributes']['href']);
-        $query = $url->getOption('query');
-        if ($page = Drupal::service('pager.parameters')->findPage()) {
-          if ($page > 0) {
-            $query['page'] = $page;
-          }
-        }
-        if (is_array($query)) {
-          $url->setOption('query', $query);
-        }
-        $attachment[0]['#attributes']['href'] = $url->toString();
-
-        // So the URL can be different for each page.
-        $cache->addCacheContexts(['url.query_args.pagers']);
-      }
-    }
-  }
-
-  $cache->applyTo($attachments);
+  \Drupal::service(PagerMetadataHooks::class)->pageAttachmentsAlter($attachments);
 }
 
 /**
  * Implements hook_preprocess_HOOK().
  */
+#[LegacyHook]
 function pager_metadata_preprocess_pager(&$pager) {
-  _pager_metadata_relations_to_head($pager);
+  \Drupal::service(PagerMetadataHooks::class)->preprocessPager($pager);
 }
 
 /**
  * Implements hook_preprocess_HOOK().
  */
+#[LegacyHook]
 function pager_metadata_preprocess_views_infinite_scroll_pager(&$pager) {
-  _pager_metadata_relations_to_head($pager);
+  \Drupal::service(PagerMetadataHooks::class)->preprocessViewsInfiniteScrollPager($pager);
 }
 
 /**
@@ -94,9 +75,7 @@ function _pager_metadata_relations_to_head(array &$pager) {
  *
  * @link https://www.drupal.org/project/drupal/issues/2769953#comment-12561253
  */
+#[LegacyHook]
 function pager_metadata_block_build_alter(array &$build, BlockPluginInterface $block) {
-  if ($block->getBaseId() == 'views_block') {
-    // To make sure the pager preprocess can still insert something the head.
-    $build['#create_placeholder'] = FALSE;
-  }
+  \Drupal::service(PagerMetadataHooks::class)->blockBuildAlter($build, $block);
 }
diff --git a/pager_metadata.services.yml b/pager_metadata.services.yml
new file mode 100644
index 0000000..884e0c9
--- /dev/null
+++ b/pager_metadata.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\pager_metadata\Hook\PagerMetadataHooks:
+    class: Drupal\pager_metadata\Hook\PagerMetadataHooks
+    autowire: true
diff --git a/src/Hook/PagerMetadataHooks.php b/src/Hook/PagerMetadataHooks.php
new file mode 100644
index 0000000..7b16241
--- /dev/null
+++ b/src/Hook/PagerMetadataHooks.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Drupal\pager_metadata\Hook;
+
+use Drupal\Core\Block\BlockPluginInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Site\Settings;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for pager_metadata.
+ */
+class PagerMetadataHooks {
+
+  /**
+   * Implements hook_page_attachments_alter().
+   */
+  #[Hook('page_attachments_alter')]
+  public function pageAttachmentsAlter(array &$attachments) {
+    $cache = CacheableMetadata::createFromRenderArray($attachments);
+    if (Settings::get('pager_metadata_alter_canonical', TRUE)) {
+      foreach ($attachments['#attached']['html_head'] as &$attachment) {
+        if ($attachment[1] == 'canonical_url') {
+          $url = Url::fromUri($attachment[0]['#attributes']['href']);
+          $query = $url->getOption('query');
+          if ($page = \Drupal::service('pager.parameters')->findPage()) {
+            if ($page > 0) {
+              $query['page'] = $page;
+            }
+          }
+          if (is_array($query)) {
+            $url->setOption('query', $query);
+          }
+          $attachment[0]['#attributes']['href'] = $url->toString();
+          // So the URL can be different for each page.
+          $cache->addCacheContexts([
+            'url.query_args.pagers',
+          ]);
+        }
+      }
+    }
+    $cache->applyTo($attachments);
+  }
+
+  /**
+   * Implements hook_preprocess_HOOK().
+   */
+  #[Hook('preprocess_pager')]
+  public function preprocessPager(&$pager) {
+    _pager_metadata_relations_to_head($pager);
+  }
+
+  /**
+   * Implements hook_preprocess_HOOK().
+   */
+  #[Hook('preprocess_views_infinite_scroll_pager')]
+  public function preprocessViewsInfiniteScrollPager(&$pager) {
+    _pager_metadata_relations_to_head($pager);
+  }
+
+  /**
+   * Implements hook_block_build_alter().
+   *
+   * @link https://www.drupal.org/project/drupal/issues/2769953#comment-12561253
+   */
+  #[Hook('block_build_alter')]
+  public function blockBuildAlter(array &$build, BlockPluginInterface $block) {
+    if ($block->getBaseId() == 'views_block') {
+      // To make sure the pager preprocess can still insert something the head.
+      $build['#create_placeholder'] = FALSE;
+    }
+  }
+
+}
