diff --git a/includes/widget_engine.theme.inc b/includes/widget_engine.theme.inc
index a78fa2c..3157b47 100644
--- a/includes/widget_engine.theme.inc
+++ b/includes/widget_engine.theme.inc
@@ -4,34 +4,17 @@
  * @file
  * Preprocess for widget_engine module.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\widget_engine\Hook\WidgetEngineThemeHooks;
 use Drupal\Core\Render\Element;
 
 /**
  * Implements hook_theme().
  */
-function widget_engine_theme($existing, $type, $theme, $path) {
-  $theme = [];
-  $theme['widget'] = [
-    'render element' => 'elements',
-    'template' => 'widget',
-  ];
-  $theme['widget_content_add_list'] = [
-    'render element' => 'content',
-    'variables' => ['content' => NULL],
-  ];
-  $theme['html__widget_engine'] = [
-    'template' => 'html--widget-engine',
-    'render element' => 'html',
-    'preprocess functions' => ['template_preprocess_html'],
-  ];
-  $theme['page__widget_engine'] = [
-    'template' => 'page--widget-engine',
-    'render element' => 'html',
-    'preprocess functions' => ['template_preprocess_page'],
-  ];
-
-  return $theme;
+#[LegacyHook]
+function widget_engine_theme($existing, $type, $theme, $path)
+{
+    return \Drupal::service(WidgetEngineThemeHooks::class)->theme($existing, $type, $theme, $path);
 }
 
 /**
@@ -56,15 +39,8 @@ function template_preprocess_widget(array &$variables) {
 /**
  * Implements hook_theme_suggestions_HOOK().
  */
-function widget_engine_theme_suggestions_widget(array $variables) {
-  $suggestions = [];
-  $entity = $variables['elements']['#widget'];
-  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
-
-  $suggestions[] = 'widget__' . $sanitized_view_mode;
-  $suggestions[] = 'widget__' . $entity->bundle();
-  $suggestions[] = 'widget__' . $entity->bundle() . '__' . $sanitized_view_mode;
-  $suggestions[] = 'widget__' . $entity->id();
-  $suggestions[] = 'widget__' . $entity->id() . '__' . $sanitized_view_mode;
-  return $suggestions;
+#[LegacyHook]
+function widget_engine_theme_suggestions_widget(array $variables)
+{
+    return \Drupal::service(WidgetEngineThemeHooks::class)->themeSuggestionsWidget($variables);
 }
diff --git a/modules/widget_engine_domain_access/src/Hook/WidgetEngineDomainAccessHooks.php b/modules/widget_engine_domain_access/src/Hook/WidgetEngineDomainAccessHooks.php
new file mode 100644
index 0000000..95534bc
--- /dev/null
+++ b/modules/widget_engine_domain_access/src/Hook/WidgetEngineDomainAccessHooks.php
@@ -0,0 +1,221 @@
+<?php
+
+namespace Drupal\widget_engine_domain_access\Hook;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+/**
+ * Hook implementations for widget_engine_domain_access.
+ */
+class WidgetEngineDomainAccessHooks
+{
+    use StringTranslationTrait;
+    /**
+     * Implements hook_help().
+     */
+    #[Hook('help')]
+    public function help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match)
+    {
+        $output = '';
+        switch ($route_name) {
+            // Main module help for the widget_engine_domain_access module.
+            case 'help.page.widget_engine_domain_access':
+                $output .= '<h3>' . $this->t('About') . '</h3>';
+                $output .= '<p>' . $this->t('Domain-based access control for widget entities') . '</p>';
+                break;
+            default:
+        }
+        return $output;
+    }
+    /**
+     * Implements hook_entity_type_build().
+     */
+    #[Hook('entity_type_build')]
+    public function entityTypeBuild(array &$entity_types)
+    {
+        /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
+        if (isset($entity_types['widget'])) {
+            // We should reset access handler for widget entity for handling
+            // domain-based access restriction. With standard Widget access handling
+            // it won't work if user has standard Widget create/update permissions.
+            // @TODO: find more elegant way to handle domain-based access restrictions.
+            $entity_types['widget']->setHandlerClass('access', '\Drupal\widget_engine_domain_access\WidgetDomainAccessControlHandler');
+        }
+    }
+    /**
+     * Implements hook_domain_references_alter().
+     */
+    #[Hook('domain_references_alter')]
+    public function domainReferencesAlter($query, $account, $context)
+    {
+        /*  @var \Drupal\Core\Entity\Query\QueryInterface $query */
+        /*  @var \Drupal\Core\Session\AccountInterface $account */
+        // Restrict domains by editorial assignment.
+        if ($context['field_type'] != 'editor') {
+            return;
+        }
+        switch ($context['entity_type']) {
+            case 'widget':
+                if ($account->hasPermission('save widgets on any domain')) {
+                    break;
+                } elseif ($account->hasPermission('save widgets on any assigned domain')) {
+                    if (!empty($account->get(DOMAIN_ACCESS_ALL_FIELD)->value)) {
+                        break;
+                    }
+                    $allowed = \Drupal::service('domain_access.manager')->getAccessValues($account);
+                    $query->condition('id', array_keys($allowed), 'IN');
+                } else {
+                    // Remove all options.
+                    $query->condition('id', '-no-possible-match-');
+                }
+                break;
+            default:
+        }
+    }
+    /**
+     * Implements hook_ENTITY_TYPE_access().
+     */
+    #[Hook('widget_access')]
+    public function widgetAccess(\Drupal\Core\Entity\EntityInterface $widget, $operation, \Drupal\Core\Session\AccountInterface $account)
+    {
+        static $active_domain;
+        if (!isset($active_domain)) {
+            // Ensure that the loader has run. In some tests, the kernel event has not.
+            $active = \Drupal::service('domain.negotiator')->getActiveDomain();
+            if (empty($active)) {
+                $active = \Drupal::service('domain.negotiator')->getActiveDomain(TRUE);
+            }
+            /** @var \Drupal\domain\Entity\Domain $active_domain */
+            $active_domain = $active;
+        }
+        // Check to see that we have a valid active domain.
+        // Without one, we cannot assert an opinion about access.
+        if (empty($active_domain->getDomainId())) {
+            return \Drupal\Core\Access\AccessResult::neutral();
+        }
+        $type = $widget->bundle();
+        $manager = \Drupal::service('widget_engine_domain_access.manager');
+        $allowed = FALSE;
+        if ($operation == 'view' && $manager->checkEntityAccess($widget, $account)) {
+            /** @var \Drupal\user\UserInterface $user */
+            if ($widget->isPublished()) {
+                $allowed = TRUE;
+            } elseif ($account->hasPermission('view unpublished domain widgets')) {
+                $allowed = TRUE;
+            }
+        }
+        if ($operation == 'update') {
+            if ($account->hasPermission('update ' . $type . ' widget on assigned domains') && $manager->checkEntityAccess($widget, $account)) {
+                $allowed = TRUE;
+            } elseif ($account->hasPermission('edit domain widgets') && $manager->checkEntityAccess($widget, $account)) {
+                $allowed = TRUE;
+            }
+        }
+        if ($operation == 'delete') {
+            if ($account->hasPermission('delete ' . $type . ' widget on assigned domains') && $manager->checkEntityAccess($widget, $account)) {
+                $allowed = TRUE;
+            } elseif ($account->hasPermission('delete domain widgets') && $manager->checkEntityAccess($widget, $account)) {
+                $allowed = TRUE;
+            }
+        }
+        if ($allowed) {
+            return \Drupal\Core\Access\AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($widget);
+        }
+        // No opinion.
+        return \Drupal\Core\Access\AccessResult::neutral();
+    }
+    /**
+     * Implements hook_ENTITY_TYPE_create_access().
+     */
+    #[Hook('widget_create_access')]
+    public function widgetCreateAccess(\Drupal\Core\Session\AccountInterface $account, $context, $entity_bundle)
+    {
+        // Check to see that we have a valid active domain.
+        // Without one, we cannot assert an opinion about access.
+        /** @var \Drupal\domain\DomainInterface $active */
+        if ($active = \Drupal::service('domain.negotiator')->getActiveDomain()) {
+            $id = $active->getDomainId();
+        } else {
+            return \Drupal\Core\Access\AccessResult::neutral();
+        }
+        // Load the full user record.
+        $user = \Drupal::entityTypeManager()->getStorage('user')->load($account->id());
+        $user_domains = \Drupal::service('domain_access.manager')->getAccessValues($user);
+        if (($account->hasPermission('create ' . $entity_bundle . ' widget on assigned domains') || $account->hasPermission('create domain widgets')) && in_array($id, $user_domains)) {
+            // Note the cache context here!
+            return \Drupal\Core\Access\AccessResult::allowed()->addCacheContexts([
+                'user.permissions',
+                'url.site',
+            ]);
+        }
+        // No opinion.
+        return \Drupal\Core\Access\AccessResult::neutral();
+    }
+    /**
+     * Implements hook_entity_field_access_alter().
+     */
+    #[Hook('entity_field_access_alter')]
+    public function entityFieldAccessAlter(array &$grants, array $context)
+    {
+        /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
+        $field_definition = $context['field_definition'];
+        if ($field_definition->getTargetEntityTypeId() == 'widget' && ($field_definition->getName() == DOMAIN_ACCESS_ALL_FIELD || $field_definition->getName() == DOMAIN_ACCESS_FIELD)) {
+            switch ($field_definition->getName()) {
+                case DOMAIN_ACCESS_ALL_FIELD:
+                    $access = \Drupal\Core\Access\AccessResult::forbiddenIf(!$context['account']->hasPermission('save widgets on any domain'));
+                    break;
+                case DOMAIN_ACCESS_FIELD:
+                    $access = \Drupal\Core\Access\AccessResult::allowedIfHasPermissions($context['account'], [
+                        'save widgets on any domain',
+                        'save widgets on any assigned domain',
+                    ], 'OR');
+                    if (!$access->isAllowed()) {
+                        $access = \Drupal\Core\Access\AccessResult::forbidden();
+                    }
+                    break;
+                default:
+            }
+            $grants['domain_access'] = $access;
+        }
+    }
+    /**
+     * Implements hook_ENTITY_TYPE_insert().
+     *
+     * Creates our fields when new widget types are created.
+     */
+    #[Hook('widget_type_insert')]
+    public function widgetTypeInsert(\Drupal\Core\Entity\EntityInterface $entity)
+    {
+        /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
+        if (!$entity->isSyncing()) {
+            // Do not fire hook when config sync in progress.
+            widget_engine_domain_access_confirm_fields('widget', $entity->id());
+        }
+    }
+    /**
+     * Implements hook_views_data_alter().
+     */
+    #[Hook('views_data_alter')]
+    public function viewsDataAlter(array &$data)
+    {
+        $table = 'widget__' . DOMAIN_ACCESS_FIELD;
+        $data[$table][DOMAIN_ACCESS_FIELD]['field']['id'] = 'domain_access_field';
+        $data[$table][DOMAIN_ACCESS_FIELD . '_target_id']['filter']['id'] = 'domain_access_filter';
+        $data[$table][DOMAIN_ACCESS_FIELD . '_target_id']['argument']['id'] = 'domain_access_argument';
+        // Current domain filter.
+        $data[$table]['current_all'] = [
+            'title' => $this->t('Current domain'),
+            'group' => $this->t('Domain'),
+            'filter' => [
+                'field' => DOMAIN_ACCESS_FIELD . '_target_id',
+                'id' => 'widget_engine_domain_access_current_all_filter',
+                'title' => $this->t('Available on current domain'),
+                'help' => $this->t('Filters out widgets not available on current domain (published to current domain or all affiliates).'),
+            ],
+        ];
+    }
+}
diff --git a/modules/widget_engine_domain_access/tests/src/Functional/WidgetEngineDomainBaseTest.php b/modules/widget_engine_domain_access/tests/src/Functional/WidgetEngineDomainBaseTest.php
index ac636d1..3c8e7fa 100644
--- a/modules/widget_engine_domain_access/tests/src/Functional/WidgetEngineDomainBaseTest.php
+++ b/modules/widget_engine_domain_access/tests/src/Functional/WidgetEngineDomainBaseTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\widget_engine_domain_access\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Core\Entity\EntityAccessControlHandlerInterface;
 use Drupal\Tests\BrowserTestBase;
 use Drupal\Tests\domain\Traits\DomainTestTrait;
@@ -11,6 +13,8 @@ use Drupal\widget_engine\Entity\Widget;
 /**
  * @group widget_engine_domain_access
  */
+#[Group('widget_engine_domain_access')]
+#[RunTestsInSeparateProcesses]
 class WidgetEngineDomainBaseTest extends BrowserTestBase {
 
   use WidgetTypeCreationTrait;
diff --git a/modules/widget_engine_domain_access/tests/src/Kernel/WidgetEngineDomainAccessPermissionsTest.php b/modules/widget_engine_domain_access/tests/src/Kernel/WidgetEngineDomainAccessPermissionsTest.php
index cc64acb..02360dc 100644
--- a/modules/widget_engine_domain_access/tests/src/Kernel/WidgetEngineDomainAccessPermissionsTest.php
+++ b/modules/widget_engine_domain_access/tests/src/Kernel/WidgetEngineDomainAccessPermissionsTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\widget_engine_domain_access\Kernel;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\Tests\domain\Traits\DomainTestTrait;
 use Drupal\Tests\widget_engine\Traits\WidgetTypeCreationTrait;
@@ -9,6 +11,8 @@ use Drupal\Tests\widget_engine\Traits\WidgetTypeCreationTrait;
 /**
  * @group widget_engine_domain_access
  */
+#[Group('widget_engine_domain_access')]
+#[RunTestsInSeparateProcesses]
 class WidgetEngineDomainAccessPermissionsTest extends KernelTestBase {
 
   use WidgetTypeCreationTrait;
diff --git a/modules/widget_engine_domain_access/widget_engine_domain_access.module b/modules/widget_engine_domain_access/widget_engine_domain_access.module
index c8129e1..59440c6 100755
--- a/modules/widget_engine_domain_access/widget_engine_domain_access.module
+++ b/modules/widget_engine_domain_access/widget_engine_domain_access.module
@@ -4,7 +4,8 @@
  * @file
  * Domain-based access control for widget entities.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\widget_engine_domain_access\Hook\WidgetEngineDomainAccessHooks;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -13,186 +14,54 @@ use Drupal\Core\Session\AccountInterface;
 /**
  * Implements hook_help().
  */
-function widget_engine_domain_access_help($route_name, RouteMatchInterface $route_match) {
-  $output = '';
-  switch ($route_name) {
-    // Main module help for the widget_engine_domain_access module.
-    case 'help.page.widget_engine_domain_access':
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('Domain-based access control for widget entities') . '</p>';
-      break;
-
-    default:
-  }
-
-  return $output;
+#[LegacyHook]
+function widget_engine_domain_access_help($route_name, RouteMatchInterface $route_match)
+{
+    return \Drupal::service(WidgetEngineDomainAccessHooks::class)->help($route_name, $route_match);
 }
 
 /**
  * Implements hook_entity_type_build().
  */
+#[LegacyHook]
 function widget_engine_domain_access_entity_type_build(array &$entity_types) {
-  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
-  if (isset($entity_types['widget'])) {
-    // We should reset access handler for widget entity for handling
-    // domain-based access restriction. With standard Widget access handling
-    // it won't work if user has standard Widget create/update permissions.
-    // @TODO: find more elegant way to handle domain-based access restrictions.
-    $entity_types['widget']->setHandlerClass('access', '\Drupal\widget_engine_domain_access\WidgetDomainAccessControlHandler');
-  }
+  \Drupal::service(WidgetEngineDomainAccessHooks::class)->entityTypeBuild($entity_types);
 }
 
 /**
  * Implements hook_domain_references_alter().
  */
-function widget_engine_domain_access_domain_references_alter($query, $account, $context) {
-  /*  @var \Drupal\Core\Entity\Query\QueryInterface $query */
-  /*  @var \Drupal\Core\Session\AccountInterface $account */
-  // Restrict domains by editorial assignment.
-  if ($context['field_type'] != 'editor') {
-    return;
-  }
-  switch ($context['entity_type']) {
-    case 'widget':
-      if ($account->hasPermission('save widgets on any domain')) {
-        break;
-      }
-      elseif ($account->hasPermission('save widgets on any assigned domain')) {
-        if (!empty($account->get(DOMAIN_ACCESS_ALL_FIELD)->value)) {
-          break;
-        }
-        $allowed = \Drupal::service('domain_access.manager')->getAccessValues($account);
-        $query->condition('id', array_keys($allowed), 'IN');
-      }
-      else {
-        // Remove all options.
-        $query->condition('id', '-no-possible-match-');
-      }
-      break;
-
-    default:
-  }
+#[LegacyHook]
+function widget_engine_domain_access_domain_references_alter($query, $account, $context)
+{
+    \Drupal::service(WidgetEngineDomainAccessHooks::class)->domainReferencesAlter($query, $account, $context);
 }
 
 /**
  * Implements hook_ENTITY_TYPE_access().
  */
-function widget_engine_domain_access_widget_access(EntityInterface $widget, $operation, AccountInterface $account) {
-  static $active_domain;
-  if (!isset($active_domain)) {
-    // Ensure that the loader has run. In some tests, the kernel event has not.
-    $active = \Drupal::service('domain.negotiator')->getActiveDomain();
-    if (empty($active)) {
-      $active = \Drupal::service('domain.negotiator')->getActiveDomain(TRUE);
-    }
-    /** @var \Drupal\domain\Entity\Domain $active_domain */
-    $active_domain = $active;
-  }
-  // Check to see that we have a valid active domain.
-  // Without one, we cannot assert an opinion about access.
-  if (empty($active_domain->getDomainId())) {
-    return AccessResult::neutral();
-  }
-
-  $type = $widget->bundle();
-  $manager = \Drupal::service('widget_engine_domain_access.manager');
-  $allowed = FALSE;
-
-  if ($operation == 'view' && $manager->checkEntityAccess($widget, $account)) {
-    /** @var \Drupal\user\UserInterface $user */
-    if ($widget->isPublished()) {
-      $allowed = TRUE;
-    }
-    elseif ($account->hasPermission('view unpublished domain widgets')) {
-      $allowed = TRUE;
-    }
-  }
-
-  if ($operation == 'update') {
-    if ($account->hasPermission('update ' . $type . ' widget on assigned domains') && $manager->checkEntityAccess($widget, $account)) {
-      $allowed = TRUE;
-    }
-    elseif ($account->hasPermission('edit domain widgets') && $manager->checkEntityAccess($widget, $account)) {
-      $allowed = TRUE;
-    }
-  }
-
-  if ($operation == 'delete') {
-    if ($account->hasPermission('delete ' . $type . ' widget on assigned domains') && $manager->checkEntityAccess($widget, $account)) {
-      $allowed = TRUE;
-    }
-    elseif ($account->hasPermission('delete domain widgets') && $manager->checkEntityAccess($widget, $account)) {
-      $allowed = TRUE;
-    }
-  }
-
-  if ($allowed) {
-    return AccessResult::allowed()
-      ->cachePerPermissions()
-      ->cachePerUser()
-      ->addCacheableDependency($widget);
-  }
-
-  // No opinion.
-  return AccessResult::neutral();
+#[LegacyHook]
+function widget_engine_domain_access_widget_access(EntityInterface $widget, $operation, AccountInterface $account)
+{
+    return \Drupal::service(WidgetEngineDomainAccessHooks::class)->widgetAccess($widget, $operation, $account);
 }
 
 /**
  * Implements hook_ENTITY_TYPE_create_access().
  */
-function widget_engine_domain_access_widget_create_access(AccountInterface $account, $context, $entity_bundle) {
-  // Check to see that we have a valid active domain.
-  // Without one, we cannot assert an opinion about access.
-  /** @var \Drupal\domain\DomainInterface $active */
-  if ($active = \Drupal::service('domain.negotiator')->getActiveDomain()) {
-    $id = $active->getDomainId();
-  }
-  else {
-    return AccessResult::neutral();
-  }
-  // Load the full user record.
-  $user = \Drupal::entityTypeManager()->getStorage('user')->load($account->id());
-  $user_domains = \Drupal::service('domain_access.manager')->getAccessValues($user);
-  if (($account->hasPermission('create ' . $entity_bundle . ' widget on assigned domains')
-      || $account->hasPermission('create domain widgets'))
-    && in_array($id, $user_domains)) {
-    // Note the cache context here!
-    return AccessResult::allowed()->addCacheContexts(['user.permissions', 'url.site']);
-  }
-  // No opinion.
-  return AccessResult::neutral();
+#[LegacyHook]
+function widget_engine_domain_access_widget_create_access(AccountInterface $account, $context, $entity_bundle)
+{
+    return \Drupal::service(WidgetEngineDomainAccessHooks::class)->widgetCreateAccess($account, $context, $entity_bundle);
 }
 
 /**
  * Implements hook_entity_field_access_alter().
  */
-function widget_engine_domain_access_entity_field_access_alter(array &$grants, array $context) {
-  /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
-  $field_definition = $context['field_definition'];
-  if ($field_definition->getTargetEntityTypeId() == 'widget'
-    && ($field_definition->getName() == DOMAIN_ACCESS_ALL_FIELD
-      || $field_definition->getName() == DOMAIN_ACCESS_FIELD )) {
-    switch ($field_definition->getName()) {
-      case DOMAIN_ACCESS_ALL_FIELD:
-        $access = AccessResult::forbiddenIf(!$context['account']->hasPermission('save widgets on any domain'));
-        break;
-
-      case DOMAIN_ACCESS_FIELD:
-        $access = AccessResult::allowedIfHasPermissions($context['account'], [
-          'save widgets on any domain',
-          'save widgets on any assigned domain',
-        ], 'OR');
-
-        if (!$access->isAllowed()) {
-          $access = AccessResult::forbidden();
-        }
-
-        break;
-
-      default:
-    }
-    $grants['domain_access'] = $access;
-  }
+#[LegacyHook]
+function widget_engine_domain_access_entity_field_access_alter(array &$grants, array $context)
+{
+    \Drupal::service(WidgetEngineDomainAccessHooks::class)->entityFieldAccessAlter($grants, $context);
 }
 
 /**
@@ -200,12 +69,9 @@ function widget_engine_domain_access_entity_field_access_alter(array &$grants, a
  *
  * Creates our fields when new widget types are created.
  */
+#[LegacyHook]
 function widget_engine_domain_access_widget_type_insert(EntityInterface $entity) {
-  /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
-  if (!$entity->isSyncing()) {
-    // Do not fire hook when config sync in progress.
-    widget_engine_domain_access_confirm_fields('widget', $entity->id());
-  }
+  \Drupal::service(WidgetEngineDomainAccessHooks::class)->widgetTypeInsert($entity);
 }
 
 /**
@@ -288,21 +154,8 @@ function widget_engine_domain_access_confirm_fields($entity_type, $bundle, $text
 /**
  * Implements hook_views_data_alter().
  */
-function widget_engine_domain_access_views_data_alter(array &$data) {
-  $table = 'widget__' . DOMAIN_ACCESS_FIELD;
-  $data[$table][DOMAIN_ACCESS_FIELD]['field']['id'] = 'domain_access_field';
-  $data[$table][DOMAIN_ACCESS_FIELD . '_target_id']['filter']['id'] = 'domain_access_filter';
-  $data[$table][DOMAIN_ACCESS_FIELD . '_target_id']['argument']['id'] = 'domain_access_argument';
-
-  // Current domain filter.
-  $data[$table]['current_all'] = [
-    'title' => t('Current domain'),
-    'group' => t('Domain'),
-    'filter' => [
-      'field' => DOMAIN_ACCESS_FIELD . '_target_id',
-      'id' => 'widget_engine_domain_access_current_all_filter',
-      'title' => t('Available on current domain'),
-      'help' => t('Filters out widgets not available on current domain (published to current domain or all affiliates).'),
-    ],
-  ];
+#[LegacyHook]
+function widget_engine_domain_access_views_data_alter(array &$data)
+{
+    \Drupal::service(WidgetEngineDomainAccessHooks::class)->viewsDataAlter($data);
 }
diff --git a/modules/widget_engine_domain_access/widget_engine_domain_access.services.yml b/modules/widget_engine_domain_access/widget_engine_domain_access.services.yml
index a146f9b..54c4763 100644
--- a/modules/widget_engine_domain_access/widget_engine_domain_access.services.yml
+++ b/modules/widget_engine_domain_access/widget_engine_domain_access.services.yml
@@ -4,3 +4,7 @@ services:
     tags:
       - { name: persist }
     arguments: ['@domain.negotiator', '@module_handler', '@entity_type.manager']
+
+  Drupal\widget_engine_domain_access\Hook\WidgetEngineDomainAccessHooks:
+    class: Drupal\widget_engine_domain_access\Hook\WidgetEngineDomainAccessHooks
+    autowire: true
diff --git a/modules/widget_engine_entity_form/includes/widget_engine_entity_form.theme.inc b/modules/widget_engine_entity_form/includes/widget_engine_entity_form.theme.inc
index 8000712..cc85f97 100644
--- a/modules/widget_engine_entity_form/includes/widget_engine_entity_form.theme.inc
+++ b/modules/widget_engine_entity_form/includes/widget_engine_entity_form.theme.inc
@@ -1,5 +1,8 @@
 <?php
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\widget_engine_entity_form\Hook\WidgetEngineEntityFormThemeHooks;
+
 /**
  * @file
  * Preprocess for widget_engine_entity_form module.
@@ -8,14 +11,10 @@
 /**
  * Implements hook_theme().
  */
-function widget_engine_entity_form_theme($existing, $type, $theme, $path) {
-  $theme = [];
-  $theme['widget__widget_form'] = array(
-    'base hook' => 'widget',
-    'template' => 'widget--widget-form',
-  );
-
-  return $theme;
+#[LegacyHook]
+function widget_engine_entity_form_theme($existing, $type, $theme, $path)
+{
+    return \Drupal::service(WidgetEngineEntityFormThemeHooks::class)->theme($existing, $type, $theme, $path);
 }
 
 /**
@@ -23,12 +22,10 @@ function widget_engine_entity_form_theme($existing, $type, $theme, $path) {
  *
  * Preprocess to widget entity.
  */
-function widget_engine_preprocess_widget(&$vars) {
-  $view_mode = $vars['elements']['#view_mode'];
-  $callback = '_widget_engine_preprocess_' . $view_mode;
-  if (function_exists($callback)) {
-    $callback($vars);
-  }
+#[LegacyHook]
+function widget_engine_preprocess_widget(&$vars)
+{
+    \Drupal::service(WidgetEngineEntityFormThemeHooks::class)->widgetEnginePreprocessWidget($vars);
 }
 
 /**
diff --git a/modules/widget_engine_entity_form/src/Hook/WidgetEngineEntityFormHooks.php b/modules/widget_engine_entity_form/src/Hook/WidgetEngineEntityFormHooks.php
new file mode 100644
index 0000000..0612a02
--- /dev/null
+++ b/modules/widget_engine_entity_form/src/Hook/WidgetEngineEntityFormHooks.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\widget_engine_entity_form\Hook;
+
+use Drupal\Core\Ajax\InvokeCommand;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\entity_browser\Form\WidgetsConfig;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+/**
+ * Hook implementations for widget_engine_entity_form.
+ */
+class WidgetEngineEntityFormHooks
+{
+    use StringTranslationTrait;
+    /**
+     * Implements hook_ajax_render_alter().
+     */
+    #[Hook('ajax_render_alter')]
+    public function ajaxRenderAlter(&$commands)
+    {
+        $selector = '.field--widget-widget-entity-browser-entity-reference';
+        $command = new \Drupal\Core\Ajax\InvokeCommand($selector, 'trigger', [
+            'afterSaveWidget',
+        ]);
+        $commands[] = $command->render();
+    }
+    /**
+     * Implements hook_form_FORM_ID_alter().
+     */
+    #[Hook('form_entity_browser_widgets_config_form_alter')]
+    public function formEntityBrowserWidgetsConfigFormAlter(&$form, \Drupal\Core\Form\FormStateInterface $form_state)
+    {
+        /** @var \Drupal\entity_browser\Form\WidgetsConfig $form_object */
+        $form_object = $form_state->getFormObject();
+        $entity_browser = $form_object->getEntity();
+        $widget_selector = $entity_browser->getWidgetSelector();
+        if ($widget_selector->getPluginId() == 'select_add_tabs') {
+            // Add specific description.
+            $description = $form['widget']['#description'];
+            $widget_note = '<strong>' . $this->t('NOTE: For "Select&Add tabs" widget selector plugin you should observe the following rules:') . '</strong><ul>';
+            $widget_note .= '<li>' . $this->t('At first place you should place "View" widget plugin.') . '</li>';
+            $widget_note .= '<li>' . $this->t('On other places you should locate "Entity form" widget plugin(s).') . '</li>';
+            $widget_note .= '</ul>';
+            $description["#items"][] = [
+                '#markup' => $widget_note,
+            ];
+            $form['widget']['#description'] = $description;
+            // Add validation.
+            $form['actions']['submit']['#validate'][] = 'widget_engine_entity_form_select_add_tabs_validate';
+        }
+    }
+}
diff --git a/modules/widget_engine_entity_form/src/Hook/WidgetEngineEntityFormThemeHooks.php b/modules/widget_engine_entity_form/src/Hook/WidgetEngineEntityFormThemeHooks.php
new file mode 100644
index 0000000..16e3979
--- /dev/null
+++ b/modules/widget_engine_entity_form/src/Hook/WidgetEngineEntityFormThemeHooks.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\widget_engine_entity_form\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for widget_engine_entity_form.
+ */
+class WidgetEngineEntityFormThemeHooks
+{
+    /**
+     * @file
+     * Preprocess for widget_engine_entity_form module.
+     */
+    /**
+     * Implements hook_theme().
+     */
+    #[Hook('theme')]
+    public function theme($existing, $type, $theme, $path)
+    {
+        $theme = [
+        ];
+        $theme['widget__widget_form'] = [
+            'base hook' => 'widget',
+            'template' => 'widget--widget-form',
+        ];
+        return $theme;
+    }
+    /**
+     * Implements hook_preprocess_HOOK().
+     *
+     * Preprocess to widget entity.
+     */
+    #[Hook('preprocess_widget', module: 'widget_engine')]
+    public function widgetEnginePreprocessWidget(&$vars)
+    {
+        $view_mode = $vars['elements']['#view_mode'];
+        $callback = '_widget_engine_preprocess_' . $view_mode;
+        if (function_exists($callback)) {
+            $callback($vars);
+        }
+    }
+}
diff --git a/modules/widget_engine_entity_form/src/Plugin/EntityBrowser/WidgetSelector/SelectAddTabs.php b/modules/widget_engine_entity_form/src/Plugin/EntityBrowser/WidgetSelector/SelectAddTabs.php
index 733b305..d554a16 100644
--- a/modules/widget_engine_entity_form/src/Plugin/EntityBrowser/WidgetSelector/SelectAddTabs.php
+++ b/modules/widget_engine_entity_form/src/Plugin/EntityBrowser/WidgetSelector/SelectAddTabs.php
@@ -19,7 +19,7 @@ class SelectAddTabs extends WidgetSelectorBase {
   /**
    * {@inheritdoc}
    */
-  public function getForm(array &$form = array(), FormStateInterface &$form_state = NULL) {
+  public function getForm(array &$form = array(), ?FormStateInterface &$form_state = NULL) {
     $element = [];
     $element_index = 0;
     $browser_widgets = $this->getBrowserWidgets();
diff --git a/modules/widget_engine_entity_form/src/Plugin/Field/FieldWidget/WidgetEngineEntityReferenceBrowserWidget.php b/modules/widget_engine_entity_form/src/Plugin/Field/FieldWidget/WidgetEngineEntityReferenceBrowserWidget.php
index a5351fd..01c3b2b 100644
--- a/modules/widget_engine_entity_form/src/Plugin/Field/FieldWidget/WidgetEngineEntityReferenceBrowserWidget.php
+++ b/modules/widget_engine_entity_form/src/Plugin/Field/FieldWidget/WidgetEngineEntityReferenceBrowserWidget.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\widget_engine_entity_form\Plugin\Field\FieldWidget;
 
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\filter\FilterFormatRepositoryInterface;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Ajax\AjaxResponse;
@@ -90,7 +92,7 @@ class WidgetEngineEntityReferenceBrowserWidget extends EntityReferenceBrowserWid
       '#type' => 'text_format',
       '#title' => $this->t('Text placeholder'),
       '#description' => $this->t('This text will be show when field is empty.'),
-      '#format' => !empty($placeholder_default_value) ? $placeholder_default_value['format'] : filter_default_format(),
+      '#format' => !empty($placeholder_default_value) ? $placeholder_default_value['format'] : DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(FilterFormatRepositoryInterface::class)->getDefaultFormat()->id(), fn() => filter_default_format()),
       '#default_value' => !empty($placeholder_default_value) ? $placeholder_default_value['value'] : '',
     ];
 
@@ -221,7 +223,7 @@ class WidgetEngineEntityReferenceBrowserWidget extends EntityReferenceBrowserWid
       $element['placeholder'] = [
         '#type' => 'item',
         '#title' => '',
-        '#markup' => check_markup($placeholder['value'], $placeholder['format']),
+        '#markup' => ['#type' => 'processed_text', '#text' => $placeholder['value'], '#format' => $placeholder['format']],
         '#weight' => -1,
       ];
     }
diff --git a/modules/widget_engine_entity_form/tests/src/FunctionalJavascript/WidgetEngineEntityBrowserEntityFormTest.php b/modules/widget_engine_entity_form/tests/src/FunctionalJavascript/WidgetEngineEntityBrowserEntityFormTest.php
index 84d7be7..c28aad8 100644
--- a/modules/widget_engine_entity_form/tests/src/FunctionalJavascript/WidgetEngineEntityBrowserEntityFormTest.php
+++ b/modules/widget_engine_entity_form/tests/src/FunctionalJavascript/WidgetEngineEntityBrowserEntityFormTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\widget_engine_entity_form\FunctionalJavascript;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
 
 /**
@@ -9,6 +11,8 @@ use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
  *
  * @group widget_engine_entity_form
  */
+#[Group('widget_engine_entity_form')]
+#[RunTestsInSeparateProcesses]
 class WidgetEngineEntityBrowserEntityFormTest extends WebDriverTestBase {
 
   /**
diff --git a/modules/widget_engine_entity_form/widget_engine_entity_form.module b/modules/widget_engine_entity_form/widget_engine_entity_form.module
index eca19e2..9c65685 100644
--- a/modules/widget_engine_entity_form/widget_engine_entity_form.module
+++ b/modules/widget_engine_entity_form/widget_engine_entity_form.module
@@ -4,12 +4,12 @@
  * @file
  * Module file for widget_engine_entity_form.info.yml.
  */
-
 /**
  * @defgroup widget_engine_entity_form.info.yml
  * @}
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\widget_engine_entity_form\Hook\WidgetEngineEntityFormHooks;
 use Drupal\Core\Ajax\InvokeCommand;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\entity_browser\Form\WidgetsConfig;
@@ -22,34 +22,19 @@ require_once 'includes/widget_engine_entity_form.theme.inc';
 /**
  * Implements hook_ajax_render_alter().
  */
-function widget_engine_entity_form_ajax_render_alter(&$commands) {
-  $selector = '.field--widget-widget-entity-browser-entity-reference';
-  $command = new InvokeCommand($selector, 'trigger', ['afterSaveWidget']);
-  $commands[] = $command->render();
+#[LegacyHook]
+function widget_engine_entity_form_ajax_render_alter(&$commands)
+{
+    \Drupal::service(WidgetEngineEntityFormHooks::class)->ajaxRenderAlter($commands);
 }
 
 /**
  * Implements hook_form_FORM_ID_alter().
  */
-function widget_engine_entity_form_form_entity_browser_widgets_config_form_alter(&$form, FormStateInterface $form_state) {
-  /** @var \Drupal\entity_browser\Form\WidgetsConfig $form_object */
-  $form_object = $form_state->getFormObject();
-  $entity_browser = $form_object->getEntity();
-  $widget_selector = $entity_browser->getWidgetSelector();
-  if ($widget_selector->getPluginId() == 'select_add_tabs') {
-    // Add specific description.
-    $description = $form['widget']['#description'];
-    $widget_note = '<strong>' . t('NOTE: For "Select&Add tabs" widget selector plugin you should observe the following rules:') . '</strong><ul>';
-    $widget_note .= '<li>' . t('At first place you should place "View" widget plugin.') . '</li>';
-    $widget_note .= '<li>' . t('On other places you should locate "Entity form" widget plugin(s).') . '</li>';
-    $widget_note .= '</ul>';
-    $description["#items"][] = [
-      '#markup' => $widget_note,
-    ];
-    $form['widget']['#description'] = $description;
-    // Add validation.
-    $form['actions']['submit']['#validate'][] = 'widget_engine_entity_form_select_add_tabs_validate';
-  }
+#[LegacyHook]
+function widget_engine_entity_form_form_entity_browser_widgets_config_form_alter(&$form, FormStateInterface $form_state)
+{
+    \Drupal::service(WidgetEngineEntityFormHooks::class)->formEntityBrowserWidgetsConfigFormAlter($form, $form_state);
 }
 
 /**
diff --git a/modules/widget_engine_entity_form/widget_engine_entity_form.services.yml b/modules/widget_engine_entity_form/widget_engine_entity_form.services.yml
index 80f165b..3e9f690 100644
--- a/modules/widget_engine_entity_form/widget_engine_entity_form.services.yml
+++ b/modules/widget_engine_entity_form/widget_engine_entity_form.services.yml
@@ -3,3 +3,11 @@ services:
     class: Drupal\widget_engine_entity_form\Routing\RouteSubscriber
     tags:
       - { name: event_subscriber }
+
+  Drupal\widget_engine_entity_form\Hook\WidgetEngineEntityFormHooks:
+    class: Drupal\widget_engine_entity_form\Hook\WidgetEngineEntityFormHooks
+    autowire: true
+
+  Drupal\widget_engine_entity_form\Hook\WidgetEngineEntityFormThemeHooks:
+    class: Drupal\widget_engine_entity_form\Hook\WidgetEngineEntityFormThemeHooks
+    autowire: true
diff --git a/src/Controller/WidgetPreviewController.php b/src/Controller/WidgetPreviewController.php
index 101468f..1043a93 100644
--- a/src/Controller/WidgetPreviewController.php
+++ b/src/Controller/WidgetPreviewController.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\widget_engine\Controller;
 
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\File\FileExists;
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\widget_engine\Entity\Widget;
@@ -53,7 +55,7 @@ class WidgetPreviewController extends ControllerBase {
         // Prepare directory.
         $directory = "public://widget_engine_preview/{$widget->getType()}/";
         \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
-        $file = \Drupal::service('file.repository')->writeData($data, $directory . $file_name, FileSystemInterface::EXISTS_REPLACE);
+        $file = \Drupal::service('file.repository')->writeData($data, $directory . $file_name, DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.3.0', fn() => FileExists::Replace, fn() => FileSystemInterface::EXISTS_REPLACE));
 
         try {
           $widget->set('widget_preview', ['target_id' => $file->id()]);
diff --git a/src/Hook/WidgetEngineHooks.php b/src/Hook/WidgetEngineHooks.php
new file mode 100644
index 0000000..c701bcd
--- /dev/null
+++ b/src/Hook/WidgetEngineHooks.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Drupal\widget_engine\Hook;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Ajax\InvokeCommand;
+use Drupal\Core\Render\Element;
+use Drupal\image\Entity\ImageStyle;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+/**
+ * Hook implementations for widget_engine.
+ */
+class WidgetEngineHooks
+{
+    use StringTranslationTrait;
+    /**
+     * Implements hook_help().
+     */
+    #[Hook('help')]
+    public function help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match)
+    {
+        $output = '';
+        switch ($route_name) {
+            // Main module help for the widget_engine module.
+            case 'help.page.widget_engine':
+                $output .= '<h3>' . $this->t('About') . '</h3>';
+                $output .= '<p>' . $this->t('Widget engine module') . '</p>';
+                break;
+            default:
+        }
+        return $output;
+    }
+    /**
+     * Implements hook_menu_links_discovered_alter().
+     */
+    #[Hook('menu_links_discovered_alter')]
+    public function menuLinksDiscoveredAlter(&$links)
+    {
+        $links['entity.widget.add_page']['parent'] = 'system.admin_content';
+        // Add links for each widget type.
+        foreach (widget_engine_type_get_names() as $machine_name => $label) {
+            $links['entity.widget.add_form' . $machine_name] = [
+                'title' => $label,
+                'route_name' => 'entity.widget.add_form',
+                'menu_name' => 'admin',
+                'parent' => 'entity.widget.add_page',
+                'route_parameters' => [
+                    'widget_type' => $machine_name,
+                ],
+            ];
+        }
+    }
+    /**
+     * Implements hook_library_info_alter().
+     */
+    #[Hook('library_info_alter')]
+    public function libraryInfoAlter(&$libraries, $extension)
+    {
+        // Optionally use the Libraries module to determine our library paths.
+        if ($extension == 'widget_engine' && \Drupal::moduleHandler()->moduleExists('libraries')) {
+            // Include html2canvas library.
+            $html2canvas_path = libraries_get_path('html2canvas') . '/dist/html2canvas.min.js';
+            $libraries['html2canvas']['js'] = [
+                '/' . $html2canvas_path => [
+                    'minified' => 'true',
+                ],
+            ];
+            // Include es6-promise library.
+            $es6_promise_path = libraries_get_path('es6-promise') . '/dist/es6-promise.auto.min.js';
+            $libraries['es6-promise']['js'] = [
+                '/' . $es6_promise_path => [
+                    'minified' => 'true',
+                ],
+            ];
+        }
+    }
+    /**
+     * Implements hook_ajax_render_alter().
+     */
+    #[Hook('ajax_render_alter')]
+    public function ajaxRenderAlter(&$commands)
+    {
+        $selector = '.ief-widget-table';
+        $command = new \Drupal\Core\Ajax\InvokeCommand($selector, 'trigger', [
+            'afterSaveWidget',
+        ]);
+        $commands[] = $command->render();
+    }
+    /**
+     * Implements hook_entity_type_build().
+     */
+    #[Hook('entity_type_build')]
+    public function entityTypeBuild(array &$entity_types)
+    {
+        /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
+        if (isset($entity_types['widget'])) {
+            $entity_types['widget']->setHandlerClass('inline_form', '\Drupal\widget_engine\Form\WidgetInlineForm');
+        }
+    }
+    /**
+     * Implements hook_theme_registry_alter().
+     */
+    #[Hook('theme_registry_alter')]
+    public function themeRegistryAlter(&$theme_registry)
+    {
+        $theme_registry['inline_entity_form_entity_table']['function'] = 'theme_widget_engine_form_entity_table';
+    }
+}
diff --git a/src/Hook/WidgetEngineThemeHooks.php b/src/Hook/WidgetEngineThemeHooks.php
new file mode 100644
index 0000000..332e59f
--- /dev/null
+++ b/src/Hook/WidgetEngineThemeHooks.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Drupal\widget_engine\Hook;
+
+use Drupal\Core\Render\Element;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for widget_engine.
+ */
+class WidgetEngineThemeHooks
+{
+    /**
+     * Implements hook_theme().
+     */
+    #[Hook('theme')]
+    public function theme($existing, $type, $theme, $path)
+    {
+        $theme = [
+        ];
+        $theme['widget'] = [
+            'render element' => 'elements',
+            'template' => 'widget',
+        ];
+        $theme['widget_content_add_list'] = [
+            'render element' => 'content',
+            'variables' => [
+                'content' => NULL,
+            ],
+        ];
+        $theme['html__widget_engine'] = [
+            'template' => 'html--widget-engine',
+            'render element' => 'html',
+            'preprocess functions' => [
+                'template_preprocess_html',
+            ],
+        ];
+        $theme['page__widget_engine'] = [
+            'template' => 'page--widget-engine',
+            'render element' => 'html',
+            'preprocess functions' => [
+                'template_preprocess_page',
+            ],
+        ];
+        return $theme;
+    }
+    /**
+     * Implements hook_theme_suggestions_HOOK().
+     */
+    #[Hook('theme_suggestions_widget')]
+    public function themeSuggestionsWidget(array $variables)
+    {
+        $suggestions = [
+        ];
+        $entity = $variables['elements']['#widget'];
+        $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
+        $suggestions[] = 'widget__' . $sanitized_view_mode;
+        $suggestions[] = 'widget__' . $entity->bundle();
+        $suggestions[] = 'widget__' . $entity->bundle() . '__' . $sanitized_view_mode;
+        $suggestions[] = 'widget__' . $entity->id();
+        $suggestions[] = 'widget__' . $entity->id() . '__' . $sanitized_view_mode;
+        return $suggestions;
+    }
+}
diff --git a/tests/src/FunctionalJavascript/WidgetReferenceIEFComplexTest.php b/tests/src/FunctionalJavascript/WidgetReferenceIEFComplexTest.php
index 01c486e..19501f3 100644
--- a/tests/src/FunctionalJavascript/WidgetReferenceIEFComplexTest.php
+++ b/tests/src/FunctionalJavascript/WidgetReferenceIEFComplexTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\widget_engine\FunctionalJavascript;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
 use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
 use Drupal\Tests\widget_engine\Traits\WidgetTypeCreationTrait;
@@ -12,6 +14,8 @@ use Drupal\Tests\widget_engine\Traits\EntityAddWidgetFieldTrait;
  *
  * @group widget_engine
  */
+#[Group('widget_engine')]
+#[RunTestsInSeparateProcesses]
 class WidgetReferenceIEFComplexTest extends WebDriverTestBase {
 
   use ContentTypeCreationTrait;
diff --git a/tests/src/Kernel/WidgetIsPublishedTest.php b/tests/src/Kernel/WidgetIsPublishedTest.php
index e161dac..2977268 100644
--- a/tests/src/Kernel/WidgetIsPublishedTest.php
+++ b/tests/src/Kernel/WidgetIsPublishedTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\widget_engine\Kernel;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
@@ -13,6 +15,8 @@ use Drupal\widget_engine\Entity\WidgetType;
  *
  * @group widget_engine
  */
+#[Group('widget_engine')]
+#[RunTestsInSeparateProcesses]
 class WidgetIsPublishedTest extends EntityKernelTestBase {
 
   /**
diff --git a/widget_engine.install b/widget_engine.install
index 0058891..282ffe5 100644
--- a/widget_engine.install
+++ b/widget_engine.install
@@ -4,7 +4,8 @@
  * @file
  * Install file for widget_engine.
  */
-
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Entity\EntityTypeListenerInterface;
 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
@@ -32,7 +33,7 @@ function widget_engine_requirements($phase) {
         'Widget Engine requires the html2canvas library. Download the newest release
 from https://github.com/niklasvh/html2canvas releases and place it in /libraries'
       ),
-      'severity' => REQUIREMENT_ERROR,
+      'severity' => DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => RequirementSeverity::Error, fn() => REQUIREMENT_ERROR),
     );
   }
 
@@ -43,7 +44,7 @@ from https://github.com/niklasvh/html2canvas releases and place it in /libraries
             'Widget Engine requires the es6-promise library. Download the newest release
 from https://github.com/stefanpenner/es6-promise and place it in /libraries'
         ),
-        'severity' => REQUIREMENT_ERROR,
+        'severity' => DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => RequirementSeverity::Error, fn() => REQUIREMENT_ERROR),
       );
     }
 
@@ -208,5 +209,5 @@ function widget_engine_update_8202() {
  */
 function widget_engine_update_8203() {
   $config_factory = \Drupal::configFactory();
-  $config_factory->getEditable('image.style.widget_preview')->set('effects.738faeb8-06f7-4840-be56-eeed7f79f659.data.height', NULL)->save(TRUE);
+  $config_factory->getEditable('image.style.widget_preview')->set('effects.738faeb8-06f7-4840-be56-eeed7f79f659.data.height', NULL)->save();
 }
diff --git a/widget_engine.module b/widget_engine.module
index dad114d..33f0243 100644
--- a/widget_engine.module
+++ b/widget_engine.module
@@ -4,7 +4,8 @@
  * @file
  * Contains widget_engine.module.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\widget_engine\Hook\WidgetEngineHooks;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Ajax\InvokeCommand;
 use Drupal\Core\Render\Element;
@@ -18,19 +19,10 @@ require_once 'includes/widget_engine.theme.inc';
 /**
  * Implements hook_help().
  */
-function widget_engine_help($route_name, RouteMatchInterface $route_match) {
-  $output = '';
-  switch ($route_name) {
-    // Main module help for the widget_engine module.
-    case 'help.page.widget_engine':
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('Widget engine module') . '</p>';
-      break;
-
-    default:
-  }
-
-  return $output;
+#[LegacyHook]
+function widget_engine_help($route_name, RouteMatchInterface $route_match)
+{
+    return \Drupal::service(WidgetEngineHooks::class)->help($route_name, $route_match);
 }
 
 /**
@@ -50,60 +42,43 @@ function widget_engine_type_get_names() {
 /**
  * Implements hook_menu_links_discovered_alter().
  */
-function widget_engine_menu_links_discovered_alter(&$links) {
-  $links['entity.widget.add_page']['parent'] = 'system.admin_content';
-  // Add links for each widget type.
-  foreach (widget_engine_type_get_names() as $machine_name => $label) {
-    $links['entity.widget.add_form' . $machine_name] = array(
-      'title' => $label,
-      'route_name' => 'entity.widget.add_form',
-      'menu_name' => 'admin',
-      'parent' => 'entity.widget.add_page',
-      'route_parameters' => array('widget_type' => $machine_name),
-    );
-  }
+#[LegacyHook]
+function widget_engine_menu_links_discovered_alter(&$links)
+{
+    \Drupal::service(WidgetEngineHooks::class)->menuLinksDiscoveredAlter($links);
 }
 
 /**
  * Implements hook_library_info_alter().
  */
+#[LegacyHook]
 function widget_engine_library_info_alter(&$libraries, $extension) {
-  // Optionally use the Libraries module to determine our library paths.
-  if ($extension == 'widget_engine'
-    && \Drupal::moduleHandler()->moduleExists('libraries')) {
-    // Include html2canvas library.
-    $html2canvas_path = libraries_get_path('html2canvas') . '/dist/html2canvas.min.js';
-    $libraries['html2canvas']['js'] = ['/' . $html2canvas_path => ['minified' => 'true']];
-    // Include es6-promise library.
-    $es6_promise_path = libraries_get_path('es6-promise') . '/dist/es6-promise.auto.min.js';
-    $libraries['es6-promise']['js'] = ['/' . $es6_promise_path => ['minified' => 'true']];
-  }
+  \Drupal::service(WidgetEngineHooks::class)->libraryInfoAlter($libraries, $extension);
 }
 
 /**
  * Implements hook_ajax_render_alter().
  */
-function widget_engine_ajax_render_alter(&$commands) {
-  $selector = '.ief-widget-table';
-  $command = new InvokeCommand($selector, 'trigger', ['afterSaveWidget']);
-  $commands[] = $command->render();
+#[LegacyHook]
+function widget_engine_ajax_render_alter(&$commands)
+{
+    \Drupal::service(WidgetEngineHooks::class)->ajaxRenderAlter($commands);
 }
 
 /**
  * Implements hook_entity_type_build().
  */
+#[LegacyHook]
 function widget_engine_entity_type_build(array &$entity_types) {
-  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
-  if (isset($entity_types['widget'])) {
-    $entity_types['widget']->setHandlerClass('inline_form', '\Drupal\widget_engine\Form\WidgetInlineForm');
-  }
+  \Drupal::service(WidgetEngineHooks::class)->entityTypeBuild($entity_types);
 }
 
 /**
  * Implements hook_theme_registry_alter().
  */
+#[LegacyHook]
 function widget_engine_theme_registry_alter(&$theme_registry) {
-  $theme_registry['inline_entity_form_entity_table']['function'] = 'theme_widget_engine_form_entity_table';
+  \Drupal::service(WidgetEngineHooks::class)->themeRegistryAlter($theme_registry);
 }
 
 /**
diff --git a/widget_engine.services.yml b/widget_engine.services.yml
new file mode 100644
index 0000000..5d27574
--- /dev/null
+++ b/widget_engine.services.yml
@@ -0,0 +1,9 @@
+
+services:
+  Drupal\widget_engine\Hook\WidgetEngineThemeHooks:
+    class: Drupal\widget_engine\Hook\WidgetEngineThemeHooks
+    autowire: true
+
+  Drupal\widget_engine\Hook\WidgetEngineHooks:
+    class: Drupal\widget_engine\Hook\WidgetEngineHooks
+    autowire: true
