diff --git a/node_visibility.info.yml b/node_visibility.info.yml
index e7576a4..189499a 100644
--- a/node_visibility.info.yml
+++ b/node_visibility.info.yml
@@ -1,7 +1,7 @@
 name: Node Visibility
 type: module
 description: 'Adds visibility condition plugins to nodes.'
-core_version_requirement: ^8 || ^9 || ^10 || ^11
+core_version_requirement: ^10.1 || ^11 || ^12
 package: 'Access control'
 
 dependencies:
diff --git a/node_visibility.module b/node_visibility.module
index 9228c53..62b85cb 100644
--- a/node_visibility.module
+++ b/node_visibility.module
@@ -4,7 +4,8 @@
  * @file
  * Contains node_visibility.module.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\node_visibility\Hook\NodeVisibilityHooks;
 use Drupal\Component\Plugin\Exception\PluginException;
 use Drupal\Component\Serialization\Json;
 use Drupal\Core\Access\AccessResult;
@@ -15,31 +16,8 @@ use Drupal\node\NodeInterface;
 /**
  * Implements hook_node_access().
  */
-function node_visibility_node_access(NodeInterface $node, $op, AccountInterface $account) {
-  $access = AccessResult::neutral();
-  if ($op === 'view') {
-    foreach ($node->getFields() as $field) {
-      if (
-        isset($field->getValue()[0]['value']) &&
-        $field->getFieldDefinition()->getType() === 'node_visibility'
-      ) {
-        $visibility_conditions = Json::decode($field->getValue()[0]['value']);
-        foreach ($visibility_conditions as $condition_id => $condition_values) {
-          /** @var \Drupal\Core\Condition\ConditionManager $condition_manager */
-          $condition_manager = \Drupal::service('plugin.manager.condition');
-          try {
-            /** @var \Drupal\Core\Condition\ConditionInterface $condition */
-            $condition = $condition_manager->createInstance($condition_id, $condition_values);
-            $access = $condition->evaluate() ? AccessResult::neutral() : AccessResult::forbidden();
-            if ($access->isForbidden()) {
-              return $access;
-            }
-          } catch (PluginException $e) {
-            \Drupal::logger('node_visibility')->error($e->getMessage());
-          }
-        }
-      }
-    }
-  }
-  return $access;
+#[LegacyHook]
+function node_visibility_node_access(NodeInterface $node, $op, AccountInterface $account)
+{
+    return \Drupal::service(NodeVisibilityHooks::class)->nodeAccess($node, $op, $account);
 }
diff --git a/node_visibility.services.yml b/node_visibility.services.yml
new file mode 100644
index 0000000..c179c66
--- /dev/null
+++ b/node_visibility.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\node_visibility\Hook\NodeVisibilityHooks:
+    class: Drupal\node_visibility\Hook\NodeVisibilityHooks
+    autowire: true
diff --git a/src/Hook/NodeVisibilityHooks.php b/src/Hook/NodeVisibilityHooks.php
new file mode 100644
index 0000000..997d8f4
--- /dev/null
+++ b/src/Hook/NodeVisibilityHooks.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\node_visibility\Hook;
+
+use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\node\NodeInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for node_visibility.
+ */
+class NodeVisibilityHooks
+{
+    /**
+     * Implements hook_node_access().
+     */
+    #[Hook('node_access')]
+    public static function nodeAccess(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Session\AccountInterface $account)
+    {
+        $access = \Drupal\Core\Access\AccessResult::neutral();
+        if ($op === 'view') {
+            foreach ($node->getFields() as $field) {
+                if (isset($field->getValue()[0]['value']) && $field->getFieldDefinition()->getType() === 'node_visibility') {
+                    $visibility_conditions = \Drupal\Component\Serialization\Json::decode($field->getValue()[0]['value']);
+                    foreach ($visibility_conditions as $condition_id => $condition_values) {
+                        /** @var \Drupal\Core\Condition\ConditionManager $condition_manager */
+                        $condition_manager = \Drupal::service('plugin.manager.condition');
+                        try {
+                            /** @var \Drupal\Core\Condition\ConditionInterface $condition */
+                            $condition = $condition_manager->createInstance($condition_id, $condition_values);
+                            $access = $condition->evaluate() ? \Drupal\Core\Access\AccessResult::neutral() : \Drupal\Core\Access\AccessResult::forbidden();
+                            if ($access->isForbidden()) {
+                                return $access;
+                            }
+                        } catch (\Drupal\Component\Plugin\Exception\PluginException $e) {
+                            \Drupal::logger('node_visibility')->error($e->getMessage());
+                        }
+                    }
+                }
+            }
+        }
+        return $access;
+    }
+}
