diff --git a/importmap.module b/importmap.module
index 1108566..451c277 100644
--- a/importmap.module
+++ b/importmap.module
@@ -5,11 +5,8 @@
  * Hooks for the importmap module.
  */
 
-use Drupal\Component\Serialization\Json;
-use Drupal\Core\Asset\AssetQueryString;
-use Drupal\Core\Render\BubbleableMetadata;
-use Drupal\Core\Url;
-use Drupal\csp\Csp;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\importmap\Hook\ImportmapHooks;
 
 /**
  * Get a list of libraries declared by core, modules and themes.
@@ -43,90 +40,7 @@ function _importmap_get_libraries(): array {
  *
  * @phpstan-param mixed[] $attachments
  */
+#[LegacyHook]
 function importmap_page_attachments(array &$attachments): void {
-  $metadata = BubbleableMetadata::createFromRenderArray($attachments);
-
-  $importmap = [];
-  $scopes = [];
-  foreach (_importmap_get_libraries() as $libraries) {
-    foreach ($libraries as $library) {
-      foreach ($library['js'] as $js) {
-        if (isset($js['attributes']['data-importmap-name'])) {
-          if ($js['type'] === 'external') {
-            $uri = $js['data'];
-          }
-          else {
-            $url = Url::fromUri(
-              'internal:/' . $js['data'],
-              [
-                // We need this to invalidate browser cache.
-                'query' => [
-                  Drupal::state()
-                    ->get(AssetQueryString::STATE_KEY, '0') => NULL,
-                ],
-              ]
-            )->toString(TRUE);
-            $metadata->addCacheableDependency($url);
-            $uri = $url->getGeneratedUrl();
-          }
-
-          if (isset($js['attributes']['data-importmap-scope'])) {
-            $scopes[$js['attributes']['data-importmap-scope']][$js['attributes']['data-importmap-name']] = $uri;
-          }
-          else {
-            $importmap[$js['attributes']['data-importmap-name']] = $uri;
-          }
-          if (isset($js['attributes']['data-importmap-preload']) && $js['attributes']['data-importmap-preload']) {
-            $metadata->addAttachments([
-              'html_head' => [
-                [
-                  [
-                    '#tag' => 'link',
-                    '#attributes' => ['rel' => 'modulepreload', 'href' => $uri],
-                    // Must be after importmap.
-                    '#weight' => 1,
-                  ],
-                  'importmap_preload_' . $js['attributes']['data-importmap-name'],
-                ],
-              ],
-            ]);
-          }
-        }
-      }
-    }
-  }
-
-  $importmapJson = Json::encode(
-    [
-      // Must always be a JSON object, even if empty.
-      'imports' => (object) $importmap,
-      'scopes' => (object) $scopes,
-    ]
-  );
-  $metadata->addAttachments([
-    'html_head' => [
-      [
-        [
-          '#tag' => 'script',
-          '#attributes' => ['type' => 'importmap'],
-          '#value' => $importmapJson,
-          // Must be before modulepreload.
-          '#weight' => 0,
-        ],
-        'importmap_json',
-      ],
-    ],
-  ]);
-
-  if (\Drupal::moduleHandler()->moduleExists('csp')) {
-    $metadata->addAttachments([
-      'csp_hash' => [
-        'script-src-elem' => [
-          Csp::calculateHash($importmapJson) => [Csp::POLICY_UNSAFE_INLINE],
-        ],
-      ],
-    ]);
-  }
-
-  $metadata->applyTo($attachments);
+  \Drupal::service(ImportmapHooks::class)->pageAttachments($attachments);
 }
diff --git a/importmap.services.yml b/importmap.services.yml
new file mode 100644
index 0000000..9d9b709
--- /dev/null
+++ b/importmap.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\importmap\Hook\ImportmapHooks:
+    class: Drupal\importmap\Hook\ImportmapHooks
+    autowire: true
diff --git a/src/Hook/ImportmapHooks.php b/src/Hook/ImportmapHooks.php
new file mode 100644
index 0000000..c02475f
--- /dev/null
+++ b/src/Hook/ImportmapHooks.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Drupal\importmap\Hook;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Asset\AssetQueryString;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Url;
+use Drupal\csp\Csp;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for importmap.
+ */
+class ImportmapHooks
+{
+    /**
+     * Implements hook_page_attachments().
+     *
+     * @phpstan-param mixed[] $attachments
+     */
+    #[Hook('page_attachments')]
+    public static function pageAttachments(array &$attachments): void
+    {
+        $metadata = \Drupal\Core\Render\BubbleableMetadata::createFromRenderArray($attachments);
+        $importmap = [
+        ];
+        $scopes = [
+        ];
+        foreach (_importmap_get_libraries() as $libraries) {
+            foreach ($libraries as $library) {
+                foreach ($library['js'] as $js) {
+                    if (isset($js['attributes']['data-importmap-name'])) {
+                        if ($js['type'] === 'external') {
+                            $uri = $js['data'];
+                        } else {
+                            $url = \Drupal\Core\Url::fromUri('internal:/' . $js['data'], [
+                                // We need this to invalidate browser cache.
+                                'query' => [
+                                    \Drupal::state()->get(\Drupal\Core\Asset\AssetQueryString::STATE_KEY, '0') => NULL,
+                                ],
+                            ])->toString(TRUE);
+                            $metadata->addCacheableDependency($url);
+                            $uri = $url->getGeneratedUrl();
+                        }
+                        if (isset($js['attributes']['data-importmap-scope'])) {
+                            $scopes[$js['attributes']['data-importmap-scope']][$js['attributes']['data-importmap-name']] = $uri;
+                        } else {
+                            $importmap[$js['attributes']['data-importmap-name']] = $uri;
+                        }
+                        if (isset($js['attributes']['data-importmap-preload']) && $js['attributes']['data-importmap-preload']) {
+                            $metadata->addAttachments([
+                                'html_head' => [
+                                    [
+                                        [
+                                            '#tag' => 'link',
+                                            '#attributes' => [
+                                                'rel' => 'modulepreload',
+                                                'href' => $uri,
+                                            ],
+                                            // Must be after importmap.
+                                            '#weight' => 1,
+                                        ],
+                                        'importmap_preload_' . $js['attributes']['data-importmap-name'],
+                                    ],
+                                ],
+                            ]);
+                        }
+                    }
+                }
+            }
+        }
+        $importmapJson = \Drupal\Component\Serialization\Json::encode([
+            // Must always be a JSON object, even if empty.
+            'imports' => (object) $importmap,
+            'scopes' => (object) $scopes,
+        ]);
+        $metadata->addAttachments([
+            'html_head' => [
+                [
+                    [
+                        '#tag' => 'script',
+                        '#attributes' => [
+                            'type' => 'importmap',
+                        ],
+                        '#value' => $importmapJson,
+                        // Must be before modulepreload.
+                        '#weight' => 0,
+                    ],
+                    'importmap_json',
+                ],
+            ],
+        ]);
+        if (\Drupal::moduleHandler()->moduleExists('csp')) {
+            $metadata->addAttachments([
+                'csp_hash' => [
+                    'script-src-elem' => [
+                        \Drupal\csp\Csp::calculateHash($importmapJson) => [
+                            \Drupal\csp\Csp::POLICY_UNSAFE_INLINE,
+                        ],
+                    ],
+                ],
+            ]);
+        }
+        $metadata->applyTo($attachments);
+    }
+}
