diff --git a/modules/ui_patterns_blocks/src/Hook/UiPatternsBlocksHooks.php b/modules/ui_patterns_blocks/src/Hook/UiPatternsBlocksHooks.php
new file mode 100644
index 0000000..4a522a5
--- /dev/null
+++ b/modules/ui_patterns_blocks/src/Hook/UiPatternsBlocksHooks.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\ui_patterns_blocks\Hook;
+
+use Drupal\Core\Plugin\Context\EntityContext;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for ui_patterns_blocks.
+ */
+class UiPatternsBlocksHooks {
+
+  /**
+   * Implements hook_plugin_filter_TYPE__CONSUMER_alter().
+   */
+  #[Hook('plugin_filter_block__layout_builder_alter')]
+  public function pluginFilterBlockLayoutBuilderAlter(array &$definitions, array $extra): void {
+    /** @var \Drupal\layout_builder\SectionStorageInterface $section_storage */
+    $section_storage = $extra['section_storage'] ?? NULL;
+    $sectionStorageContexts = $section_storage ? $section_storage->getContexts() : NULL;
+    $display = $sectionStorageContexts ? $sectionStorageContexts['display'] ?? NULL : NULL;
+    $entity_context_exists = $display && $display instanceof EntityContext;
+    _ui_patterns_blocks_remove_unsuitable_blocks($definitions, !$entity_context_exists);
+  }
+
+  /**
+   * Implements hook_plugin_filter_TYPE_alter().
+   */
+  #[Hook('plugin_filter_block_alter')]
+  public function pluginFilterBlockAlter(array &$definitions, array $extra, ?string $consumer): void {
+    if ($consumer !== 'layout_builder') {
+      // In all User interfaces except Layout builder,
+      // we remove blocks defined by ui_patterns_blocks
+      // which declare entity in context_definitions.
+      _ui_patterns_blocks_remove_unsuitable_blocks($definitions, TRUE);
+    }
+  }
+
+}
diff --git a/modules/ui_patterns_blocks/ui_patterns_blocks.info.yml b/modules/ui_patterns_blocks/ui_patterns_blocks.info.yml
index 88e352d..91ace93 100644
--- a/modules/ui_patterns_blocks/ui_patterns_blocks.info.yml
+++ b/modules/ui_patterns_blocks/ui_patterns_blocks.info.yml
@@ -1,7 +1,7 @@
 name: UI Patterns Blocks
 type: module
 description: "Use UI components as blocks plugins."
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: User interface
 dependencies:
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_blocks/ui_patterns_blocks.module b/modules/ui_patterns_blocks/ui_patterns_blocks.module
index fb34041..759cb7e 100644
--- a/modules/ui_patterns_blocks/ui_patterns_blocks.module
+++ b/modules/ui_patterns_blocks/ui_patterns_blocks.module
@@ -7,30 +7,23 @@
 
 declare(strict_types=1);
 
-use Drupal\Core\Plugin\Context\EntityContext;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\ui_patterns_blocks\Hook\UiPatternsBlocksHooks;
 
 /**
  * Implements hook_plugin_filter_TYPE__CONSUMER_alter().
  */
+#[LegacyHook]
 function ui_patterns_blocks_plugin_filter_block__layout_builder_alter(array &$definitions, array $extra): void {
-  /** @var \Drupal\layout_builder\SectionStorageInterface $section_storage */
-  $section_storage = $extra['section_storage'] ?? NULL;
-  $sectionStorageContexts = $section_storage ? $section_storage->getContexts() : NULL;
-  $display = ($sectionStorageContexts) ? $sectionStorageContexts['display'] ?? NULL : NULL;
-  $entity_context_exists = ($display && $display instanceof EntityContext);
-  _ui_patterns_blocks_remove_unsuitable_blocks($definitions, !$entity_context_exists);
+  \Drupal::service(UiPatternsBlocksHooks::class)->pluginFilterBlockLayoutBuilderAlter($definitions, $extra);
 }
 
 /**
  * Implements hook_plugin_filter_TYPE_alter().
  */
+#[LegacyHook]
 function ui_patterns_blocks_plugin_filter_block_alter(array &$definitions, array $extra, ?string $consumer): void {
-  if ($consumer !== 'layout_builder') {
-    // In all User interfaces except Layout builder,
-    // we remove blocks defined by ui_patterns_blocks
-    // which declare entity in context_definitions.
-    _ui_patterns_blocks_remove_unsuitable_blocks($definitions, TRUE);
-  }
+  \Drupal::service(UiPatternsBlocksHooks::class)->pluginFilterBlockAlter($definitions, $extra, $consumer);
 }
 
 /**
diff --git a/modules/ui_patterns_blocks/ui_patterns_blocks.services.yml b/modules/ui_patterns_blocks/ui_patterns_blocks.services.yml
new file mode 100644
index 0000000..19bc55b
--- /dev/null
+++ b/modules/ui_patterns_blocks/ui_patterns_blocks.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\ui_patterns_blocks\Hook\UiPatternsBlocksHooks:
+    class: Drupal\ui_patterns_blocks\Hook\UiPatternsBlocksHooks
+    autowire: true
diff --git a/modules/ui_patterns_field/ui_patterns_field.info.yml b/modules/ui_patterns_field/ui_patterns_field.info.yml
index cacd03c..9412272 100644
--- a/modules/ui_patterns_field/ui_patterns_field.info.yml
+++ b/modules/ui_patterns_field/ui_patterns_field.info.yml
@@ -1,7 +1,7 @@
 name: UI Patterns Field
 type: module
 description: Use UI components and UI Patterns sources in fields.
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: "User interface"
 dependencies:
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml
index e513263..d5b3c08 100644
--- a/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml
+++ b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml
@@ -1,7 +1,7 @@
 name: UI Patterns Field Formatters
 type: module
 description: Use UI components with field formatters.
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: User interface
 dependencies:
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_layouts/src/Hook/UiPatternsLayoutsHooks.php b/modules/ui_patterns_layouts/src/Hook/UiPatternsLayoutsHooks.php
new file mode 100644
index 0000000..f47d747
--- /dev/null
+++ b/modules/ui_patterns_layouts/src/Hook/UiPatternsLayoutsHooks.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\ui_patterns_layouts\Hook;
+
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\EntityContext;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for ui_patterns_layouts.
+ */
+class UiPatternsLayoutsHooks {
+
+  /**
+   * Implements hook_element_info_alter().
+   */
+  #[Hook('element_info_alter')]
+  public function elementInfoAlter(array &$types): void {
+    if (isset($types['component'])) {
+      array_unshift($types['component']['#pre_render'], [
+        'Drupal\ui_patterns_layouts\Element\ComponentAlterer',
+        'processLayoutBuilderRegions',
+      ]);
+    }
+  }
+
+  /**
+   * Implements hook_entity_view_alter().
+   *
+   * Field layout is not adding entity information to the layout.
+   * We need to add it in another step.
+   */
+  #[Hook('entity_view_alter')]
+  public function entityViewAlter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display): void {
+    if (isset($build['_field_layout']['#ui_patterns'], $build['_field_layout']['#source_contexts'])) {
+      $build['_field_layout']['#source_contexts']['entity'] = EntityContext::fromEntity($entity);
+      $build['_field_layout']['#source_contexts']['bundle'] = new Context(ContextDefinition::create('string'), $entity->bundle() ?? '');
+    }
+  }
+
+}
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml b/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml
index 5dc047d..e7de803 100644
--- a/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml
+++ b/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml
@@ -1,7 +1,7 @@
 name: "UI Patterns Layouts"
 type: module
 description: "Use UI components as layouts plugins."
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: "User interface"
 dependencies:
   - drupal:layout_discovery
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.module b/modules/ui_patterns_layouts/ui_patterns_layouts.module
index 0db7b56..16b42a2 100644
--- a/modules/ui_patterns_layouts/ui_patterns_layouts.module
+++ b/modules/ui_patterns_layouts/ui_patterns_layouts.module
@@ -7,22 +7,17 @@
 
 declare(strict_types=1);
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\ui_patterns_layouts\Hook\UiPatternsLayoutsHooks;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Plugin\Context\Context;
-use Drupal\Core\Plugin\Context\ContextDefinition;
-use Drupal\Core\Plugin\Context\EntityContext;
 
 /**
  * Implements hook_element_info_alter().
  */
+#[LegacyHook]
 function ui_patterns_layouts_element_info_alter(array &$types): void {
-  if (isset($types['component'])) {
-    array_unshift(
-      $types['component']['#pre_render'],
-      ['Drupal\ui_patterns_layouts\Element\ComponentAlterer', 'processLayoutBuilderRegions']
-    );
-  }
+  \Drupal::service(UiPatternsLayoutsHooks::class)->elementInfoAlter($types);
 }
 
 /**
@@ -31,10 +26,7 @@ function ui_patterns_layouts_element_info_alter(array &$types): void {
  * Field layout is not adding entity information to the layout.
  * We need to add it in another step.
  */
+#[LegacyHook]
 function ui_patterns_layouts_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display): void {
-  if (isset($build['_field_layout']['#ui_patterns'], $build['_field_layout']['#source_contexts'])
-  ) {
-    $build['_field_layout']['#source_contexts']['entity'] = EntityContext::fromEntity($entity);
-    $build['_field_layout']['#source_contexts']['bundle'] = new Context(ContextDefinition::create('string'), $entity->bundle() ?? '');
-  }
+  \Drupal::service(UiPatternsLayoutsHooks::class)->entityViewAlter($build, $entity, $display);
 }
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.services.yml b/modules/ui_patterns_layouts/ui_patterns_layouts.services.yml
new file mode 100644
index 0000000..18888dd
--- /dev/null
+++ b/modules/ui_patterns_layouts/ui_patterns_layouts.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\ui_patterns_layouts\Hook\UiPatternsLayoutsHooks:
+    class: Drupal\ui_patterns_layouts\Hook\UiPatternsLayoutsHooks
+    autowire: true
diff --git a/modules/ui_patterns_legacy/src/Hook/UiPatternsLegacyHooks.php b/modules/ui_patterns_legacy/src/Hook/UiPatternsLegacyHooks.php
new file mode 100644
index 0000000..e1e108f
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/Hook/UiPatternsLegacyHooks.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for ui_patterns_legacy.
+ */
+class UiPatternsLegacyHooks {
+
+  /**
+   * Implements hook_element_info_alter().
+   */
+  #[Hook('element_info_alter')]
+  public function elementInfoAlter(array &$types): void {
+    if (isset($types['component'])) {
+      $types = _ui_patterns_legacy_clone_component_element($types, 'pattern');
+      $types = _ui_patterns_legacy_clone_component_element($types, 'pattern_preview');
+    }
+  }
+
+}
diff --git a/modules/ui_patterns_legacy/src/Template/TwigExtension.php b/modules/ui_patterns_legacy/src/Template/TwigExtension.php
index 9f832fa..6633744 100644
--- a/modules/ui_patterns_legacy/src/Template/TwigExtension.php
+++ b/modules/ui_patterns_legacy/src/Template/TwigExtension.php
@@ -24,14 +24,8 @@ class TwigExtension extends AbstractExtension {
    */
   public function getFunctions(): array {
     return [
-      new TwigFunction('pattern', [
-        $this,
-        'renderPattern',
-      ]),
-      new TwigFunction('pattern_preview', [
-        $this,
-        'renderPatternPreview',
-      ]),
+      new TwigFunction('pattern', $this->renderPattern(...)),
+      new TwigFunction('pattern_preview', $this->renderPatternPreview(...)),
     ];
   }
 
diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml
index 05705e5..fb9d2bc 100644
--- a/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml
+++ b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml
@@ -1,7 +1,7 @@
 name: "UI Patterns Legacy"
 type: module
 description: "Compatibility layer for UI Patterns 1.x components."
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: "User interface"
 dependencies:
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.module b/modules/ui_patterns_legacy/ui_patterns_legacy.module
index 51d1839..41ed5c3 100644
--- a/modules/ui_patterns_legacy/ui_patterns_legacy.module
+++ b/modules/ui_patterns_legacy/ui_patterns_legacy.module
@@ -7,14 +7,15 @@
 
 declare(strict_types=1);
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\ui_patterns_legacy\Hook\UiPatternsLegacyHooks;
+
 /**
  * Implements hook_element_info_alter().
  */
+#[LegacyHook]
 function ui_patterns_legacy_element_info_alter(array &$types): void {
-  if (isset($types['component'])) {
-    $types = _ui_patterns_legacy_clone_component_element($types, 'pattern');
-    $types = _ui_patterns_legacy_clone_component_element($types, 'pattern_preview');
-  }
+  \Drupal::service(UiPatternsLegacyHooks::class)->elementInfoAlter($types);
 }
 
 /**
diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml
index b04547e..b6c22d6 100644
--- a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml
+++ b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml
@@ -51,3 +51,7 @@ services:
     class: Drupal\ui_patterns_legacy\Service\LayoutBuilderUpdater
     arguments:
       - "@plugin.manager.sdc"
+
+  Drupal\ui_patterns_legacy\Hook\UiPatternsLegacyHooks:
+    class: Drupal\ui_patterns_legacy\Hook\UiPatternsLegacyHooks
+    autowire: true
diff --git a/modules/ui_patterns_library/src/Hook/UiPatternsLibraryHooks.php b/modules/ui_patterns_library/src/Hook/UiPatternsLibraryHooks.php
new file mode 100644
index 0000000..3270e40
--- /dev/null
+++ b/modules/ui_patterns_library/src/Hook/UiPatternsLibraryHooks.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\ui_patterns_library\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for ui_patterns_library.
+ */
+class UiPatternsLibraryHooks {
+  /**
+   * @file
+   * Contains module file.
+   */
+
+  /**
+   * Implements hook_theme().
+   */
+  #[Hook('theme')]
+  public function theme(): array {
+    return [
+      'ui_patterns_overview_page' => [
+        'variables' => [
+          'groups' => NULL,
+        ],
+      ],
+      'ui_patterns_overview_quicklinks' => [
+        'variables' => [
+          'groups' => NULL,
+        ],
+      ],
+      'ui_patterns_single_page' => [
+        'variables' => [
+          'component' => NULL,
+        ],
+      ],
+      'ui_patterns_component_metadata' => [
+        'variables' => [
+          'component' => NULL,
+        ],
+      ],
+      'ui_patterns_component_table' => [
+        'variables' => [
+          'component' => NULL,
+        ],
+      ],
+      'ui_patterns_stories_full' => [
+        'variables' => [
+          'component' => NULL,
+        ],
+      ],
+      'ui_patterns_stories_compact' => [
+        'variables' => [
+          'component' => NULL,
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * Implements hook_element_info_alter().
+   */
+  #[Hook('element_info_alter')]
+  public function elementInfoAlter(array &$types): void {
+    if (isset($types['component'])) {
+      array_unshift($types['component']['#pre_render'], 'ui_patterns_library.component_element_alter:alter');
+    }
+  }
+
+}
diff --git a/modules/ui_patterns_library/src/Template/TwigExtension.php b/modules/ui_patterns_library/src/Template/TwigExtension.php
index 47cf621..e5e809e 100644
--- a/modules/ui_patterns_library/src/Template/TwigExtension.php
+++ b/modules/ui_patterns_library/src/Template/TwigExtension.php
@@ -27,10 +27,7 @@ class TwigExtension extends AbstractExtension {
    */
   public function getFunctions(): array {
     return [
-      new TwigFunction('component_story', [
-        $this,
-        'renderComponentStory',
-      ]),
+      new TwigFunction('component_story', $this->renderComponentStory(...)),
     ];
   }
 
diff --git a/modules/ui_patterns_library/ui_patterns_library.info.yml b/modules/ui_patterns_library/ui_patterns_library.info.yml
index abec528..435c0d6 100644
--- a/modules/ui_patterns_library/ui_patterns_library.info.yml
+++ b/modules/ui_patterns_library/ui_patterns_library.info.yml
@@ -1,7 +1,7 @@
 name: "UI Patterns Library"
 type: module
 description: "Browse UI components in library pages."
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: "User interface"
 dependencies:
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_library/ui_patterns_library.install b/modules/ui_patterns_library/ui_patterns_library.install
index 133d41b..f7432f6 100644
--- a/modules/ui_patterns_library/ui_patterns_library.install
+++ b/modules/ui_patterns_library/ui_patterns_library.install
@@ -7,6 +7,8 @@
 
 declare(strict_types=1);
 
+use Drupal\Component\Utility\DeprecationHelper;
+
 /**
  * Update permission.
  */
@@ -20,7 +22,7 @@ function ui_patterns_library_update_10201(): void {
       if ($role->hasPermission('access patterns page')) {
         $role->revokePermission('access patterns page');
         $role->grantPermission('access components page');
-        $role->trustData()->save();
+        DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $role, fn() => $role->trustData())->save();
       }
     }
   }
diff --git a/modules/ui_patterns_library/ui_patterns_library.module b/modules/ui_patterns_library/ui_patterns_library.module
index 9d21cf1..d61ede0 100644
--- a/modules/ui_patterns_library/ui_patterns_library.module
+++ b/modules/ui_patterns_library/ui_patterns_library.module
@@ -7,6 +7,9 @@
 
 declare(strict_types=1);
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\ui_patterns_library\Hook\UiPatternsLibraryHooks;
+
 /**
  * @file
  * Contains module file.
@@ -15,37 +18,15 @@ declare(strict_types=1);
 /**
  * Implements hook_theme().
  */
+#[LegacyHook]
 function ui_patterns_library_theme(): array {
-  return [
-    'ui_patterns_overview_page' => [
-      'variables' => ['groups' => NULL],
-    ],
-    'ui_patterns_overview_quicklinks' => [
-      'variables' => ['groups' => NULL],
-    ],
-    'ui_patterns_single_page' => [
-      'variables' => ['component' => NULL],
-    ],
-    'ui_patterns_component_metadata' => [
-      'variables' => ['component' => NULL],
-    ],
-    'ui_patterns_component_table' => [
-      'variables' => ['component' => NULL],
-    ],
-    'ui_patterns_stories_full' => [
-      'variables' => ['component' => NULL],
-    ],
-    'ui_patterns_stories_compact' => [
-      'variables' => ['component' => NULL],
-    ],
-  ];
+  return \Drupal::service(UiPatternsLibraryHooks::class)->theme();
 }
 
 /**
  * Implements hook_element_info_alter().
  */
+#[LegacyHook]
 function ui_patterns_library_element_info_alter(array &$types): void {
-  if (isset($types['component'])) {
-    array_unshift($types['component']['#pre_render'], 'ui_patterns_library.component_element_alter:alter');
-  }
+  \Drupal::service(UiPatternsLibraryHooks::class)->elementInfoAlter($types);
 }
diff --git a/modules/ui_patterns_library/ui_patterns_library.services.yml b/modules/ui_patterns_library/ui_patterns_library.services.yml
index 47f260f..97076ea 100644
--- a/modules/ui_patterns_library/ui_patterns_library.services.yml
+++ b/modules/ui_patterns_library/ui_patterns_library.services.yml
@@ -22,3 +22,7 @@ services:
       - "@file_system"
       - "@messenger"
       - "@plugin.manager.sdc"
+
+  Drupal\ui_patterns_library\Hook\UiPatternsLibraryHooks:
+    class: Drupal\ui_patterns_library\Hook\UiPatternsLibraryHooks
+    autowire: true
diff --git a/modules/ui_patterns_ui/src/Hook/UiPatternsUiHooks.php b/modules/ui_patterns_ui/src/Hook/UiPatternsUiHooks.php
new file mode 100644
index 0000000..ec9bbb7
--- /dev/null
+++ b/modules/ui_patterns_ui/src/Hook/UiPatternsUiHooks.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Drupal\ui_patterns_ui\Hook;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns_ui\ComponentFormDisplayInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for ui_patterns_ui.
+ */
+class UiPatternsUiHooks {
+
+  /**
+   * Implements hook_component_form_display_insert().
+   */
+  #[Hook('component_form_display_insert')]
+  public function componentFormDisplayInsert(ComponentFormDisplayInterface $component_form_display): void {
+    ui_patterns_ui_component_form_display_cache_refresh();
+  }
+
+  /**
+   * Implements hook_patterns_ui_component_form_display_delete().
+   */
+  #[Hook('patterns_ui_component_form_display_delete', module: 'ui')]
+  public function componentFormDisplayDelete(ComponentFormDisplayInterface $component_form_display): void {
+    ui_patterns_ui_component_form_display_cache_refresh();
+  }
+
+  /**
+   * Implements hook_patterns_ui_component_form_display_update().
+   */
+  #[Hook('patterns_ui_component_form_display_update', module: 'ui')]
+  public function componentFormDisplayUpdate(ComponentFormDisplayInterface $component_form_display): void {
+    ui_patterns_ui_component_form_display_cache_refresh();
+  }
+
+  /**
+   * Implements hook_ui_patterns_form_alter().
+   */
+  #[Hook('ui_patterns_form_alter')]
+  public function uiPatternsFormAlter(array &$form, FormStateInterface $form_state): void {
+    if ($form['#allow_override'] ?? TRUE) {
+      $form['#type'] = 'uip_displays_form';
+    }
+  }
+
+  /**
+   * Implements hook_config_schema_info_alter().
+   */
+  #[Hook('config_schema_info_alter')]
+  public function configSchemaInfoAlter(array &$definitions): void {
+    $definitions['ui_patterns_component']['mapping']['display_id'] = [
+      'type' => 'string',
+      'label' => 'UI Patterns UI Display ID',
+    ];
+  }
+
+}
diff --git a/modules/ui_patterns_ui/ui_patterns_ui.info.yml b/modules/ui_patterns_ui/ui_patterns_ui.info.yml
index 0f2f3df..8e857c7 100644
--- a/modules/ui_patterns_ui/ui_patterns_ui.info.yml
+++ b/modules/ui_patterns_ui/ui_patterns_ui.info.yml
@@ -1,7 +1,7 @@
 name: "UI Patterns UI"
 type: module
 description: "Manage display and form settings of components."
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: "User interface (experimental)"
 lifecycle: experimental
 dependencies:
diff --git a/modules/ui_patterns_ui/ui_patterns_ui.module b/modules/ui_patterns_ui/ui_patterns_ui.module
index 305bb8a..35a568f 100644
--- a/modules/ui_patterns_ui/ui_patterns_ui.module
+++ b/modules/ui_patterns_ui/ui_patterns_ui.module
@@ -7,6 +7,8 @@
 
 declare(strict_types=1);
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\ui_patterns_ui\Hook\UiPatternsUiHooks;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\ui_patterns_ui\ComponentFormDisplayInterface;
@@ -24,41 +26,41 @@ function ui_patterns_ui_component_form_display_cache_refresh(): void {
 /**
  * Implements hook_component_form_display_insert().
  */
+#[LegacyHook]
 function ui_patterns_ui_component_form_display_insert(ComponentFormDisplayInterface $component_form_display): void {
-  ui_patterns_ui_component_form_display_cache_refresh();
+  \Drupal::service(UiPatternsUiHooks::class)->componentFormDisplayInsert($component_form_display);
 }
 
 /**
  * Implements hook_patterns_ui_component_form_display_delete().
  */
+#[LegacyHook]
 function ui_patterns_ui_component_form_display_delete(ComponentFormDisplayInterface $component_form_display): void {
-  ui_patterns_ui_component_form_display_cache_refresh();
+  \Drupal::service(UiPatternsUiHooks::class)->componentFormDisplayDelete($component_form_display);
 }
 
 /**
  * Implements hook_patterns_ui_component_form_display_update().
  */
+#[LegacyHook]
 function ui_patterns_ui_component_form_display_update(ComponentFormDisplayInterface $component_form_display): void {
-  ui_patterns_ui_component_form_display_cache_refresh();
+  \Drupal::service(UiPatternsUiHooks::class)->componentFormDisplayUpdate($component_form_display);
 }
 
 /**
  * Implements hook_ui_patterns_form_alter().
  */
+#[LegacyHook]
 function ui_patterns_ui_ui_patterns_form_alter(array &$form, FormStateInterface $form_state): void {
-  if ($form['#allow_override'] ?? TRUE) {
-    $form['#type'] = 'uip_displays_form';
-  }
+  \Drupal::service(UiPatternsUiHooks::class)->uiPatternsFormAlter($form, $form_state);
 }
 
 /**
  * Implements hook_config_schema_info_alter().
  */
+#[LegacyHook]
 function ui_patterns_ui_config_schema_info_alter(array &$definitions): void {
-  $definitions['ui_patterns_component']['mapping']['display_id'] = [
-    'type' => 'string',
-    'label' => 'UI Patterns UI Display ID',
-  ];
+  \Drupal::service(UiPatternsUiHooks::class)->configSchemaInfoAlter($definitions);
 }
 
 /**
diff --git a/modules/ui_patterns_ui/ui_patterns_ui.services.yml b/modules/ui_patterns_ui/ui_patterns_ui.services.yml
index 62e5ddc..15e0153 100644
--- a/modules/ui_patterns_ui/ui_patterns_ui.services.yml
+++ b/modules/ui_patterns_ui/ui_patterns_ui.services.yml
@@ -8,3 +8,7 @@ services:
   ui_patterns_ui.subscriber:
     class: Drupal\ui_patterns_ui\Routing\RouteSubscriber
     arguments: ['@entity_type.manager', '@plugin.manager.sdc']
+
+  Drupal\ui_patterns_ui\Hook\UiPatternsUiHooks:
+    class: Drupal\ui_patterns_ui\Hook\UiPatternsUiHooks
+    autowire: true
diff --git a/modules/ui_patterns_views/ui_patterns_views.info.yml b/modules/ui_patterns_views/ui_patterns_views.info.yml
index 5318877..71b84b1 100644
--- a/modules/ui_patterns_views/ui_patterns_views.info.yml
+++ b/modules/ui_patterns_views/ui_patterns_views.info.yml
@@ -1,7 +1,7 @@
 name: "UI Patterns Views"
 type: module
 description: "Use UI components with Views rows and styles plugins."
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
 package: "User interface"
 dependencies:
   - drupal:views
diff --git a/src/Hook/UiPatternsHooks.php b/src/Hook/UiPatternsHooks.php
new file mode 100644
index 0000000..9606f1d
--- /dev/null
+++ b/src/Hook/UiPatternsHooks.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\ui_patterns\Hook;
+
+use Drupal\Core\Field\FieldConfigInterface;
+use Drupal\ui_patterns\Plugin\UiPatterns\Source\WysiwygWidget;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for ui_patterns.
+ */
+class UiPatternsHooks {
+
+  /**
+   * Implements hook_element_info_alter().
+   */
+  #[Hook('element_info_alter')]
+  public function elementInfoAlter(array &$types): void {
+    if (isset($types['component'])) {
+      array_unshift($types['component']['#pre_render'], 'ui_patterns.component_element_alter:alter');
+      array_unshift($types['component']['#pre_render'], 'ui_patterns.component_element_builder:build');
+    }
+    if (isset($types['text_format'])) {
+      $types['text_format']['#pre_render'][] = [
+        WysiwygWidget::class,
+        'textFormat',
+      ];
+    }
+  }
+
+  /**
+   * Prepare list of block plugins returned when using consumer 'ui_patterns'.
+   *
+   * @see \Drupal\ui_patterns\Plugin\UiPatterns\Source\BlockSource::listBlockDefinitions()
+   *
+   * Implements hook_plugin_filter_TYPE__CONSUMER_alter().
+   */
+  #[Hook('plugin_filter_block__ui_patterns_alter')]
+  public function pluginFilterBlockUiPatternsAlter(array &$definitions, array $extra): void {
+    // We are not allowing 'inline_block' blocks to avoid dependencies from config
+    // entities to content entities.
+    $definitions = array_filter($definitions, static function ($definition) {
+        return $definition['id'] !== 'inline_block';
+    });
+    // Those blocks are not allowed in layout builder. Let's do the same.
+    unset($definitions['system_main_block'], $definitions['page_title_block']);
+    // Add a boolean marker '_ui_patterns_compatible' to all remaining definitions
+    // Other modules can use the same hook to modify this value.
+    // This allows to add or remove blocks.
+    $forbidden_blocks = [
+      'provider' => [
+        'layout_builder',
+        'ui_patterns_blocks',
+      ],
+    ];
+    foreach ($definitions as $id => &$definition) {
+      if (isset($definitions[$id]['_ui_patterns_compatible'])) {
+        // When a block plugin already has '_ui_patterns_compatible'
+        // It probably means it has been marked by another code.
+        // Honor what the other code has done and do not override.
+        continue;
+      }
+      $compatibilityFlag = TRUE;
+      if (in_array($definition['provider'], $forbidden_blocks['provider'], TRUE)) {
+        $compatibilityFlag = FALSE;
+      }
+      // Filter out blocks with _block_ui_hidden ?
+      $definitions[$id]['_ui_patterns_compatible'] = $compatibilityFlag;
+    }
+  }
+
+  /**
+   * Implements hook_ENTITY_TYPE_delete().
+   */
+  #[Hook('field_config_delete')]
+  public function fieldConfigDelete(FieldConfigInterface $field_config): void {
+    $sample_entity_generator = \Drupal::service('ui_patterns.sample_entity_generator');
+    $entity_type = $field_config->getTargetEntityTypeId();
+    $bundle = $field_config->getTargetBundle();
+    $sample_entity_generator->delete($entity_type, $bundle);
+    // @todo trigger an event and do this logic in UiPatternsEntitySchemaSubscriber
+    \Drupal::service('plugin.manager.ui_patterns_source')->clearCachedDefinitions();
+    \Drupal::service('plugin.manager.ui_patterns_derivable_context')->clearCachedDefinitions();
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/WysiwygWidget.php b/src/Plugin/UiPatterns/Source/WysiwygWidget.php
index b7bbb87..11a9e39 100644
--- a/src/Plugin/UiPatterns/Source/WysiwygWidget.php
+++ b/src/Plugin/UiPatterns/Source/WysiwygWidget.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
 
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\filter\FilterFormatRepositoryInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Security\TrustedCallbackInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -52,7 +54,7 @@ class WysiwygWidget extends SourcePluginBase implements TrustedCallbackInterface
     }
     $config = \Drupal::configFactory()->get('filter.settings');
     $fallback_format = $config->get('fallback_format');
-    $formats = filter_formats();
+    $formats = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(FilterFormatRepositoryInterface::class)->getAllFormats(), fn() => filter_formats());
     // We add the fallback format to the list of options if removed.
     if (array_key_exists($fallback_format, $formats)
       && !array_key_exists($fallback_format, $element['format']['format']['#options'])) {
@@ -101,7 +103,7 @@ class WysiwygWidget extends SourcePluginBase implements TrustedCallbackInterface
       $element['#format'] = $value['format'];
     }
     else {
-      $element['#format'] = filter_fallback_format();
+      $element['#format'] = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(FilterFormatRepositoryInterface::class)->getFallbackFormatId(), fn() => filter_fallback_format());
     }
     $form['value'] = $element;
     $this->addRequired($form['value']);
diff --git a/src/Template/TwigExtension.php b/src/Template/TwigExtension.php
index 5acaa5b..8e300df 100644
--- a/src/Template/TwigExtension.php
+++ b/src/Template/TwigExtension.php
@@ -61,9 +61,9 @@ class TwigExtension extends AbstractExtension {
   public function getFunctions() {
     return [
       // For ComponentNodeVisitorBeforeSdc.
-      new TwigFunction('_ui_patterns_normalize_props', [$this, 'normalizeProps'], ['needs_context' => TRUE]),
+      new TwigFunction('_ui_patterns_normalize_props', $this->normalizeProps(...), ['needs_context' => TRUE]),
       // For ComponentNodeVisitorAfterSdc.
-      new TwigFunction('_ui_patterns_preprocess_props', [$this, 'preprocessProps'], ['needs_context' => TRUE]),
+      new TwigFunction('_ui_patterns_preprocess_props', $this->preprocessProps(...), ['needs_context' => TRUE]),
       // Overrides Twig's core include() — see ::include(). Options match
       // CoreExtension's registration so direct printing is unaffected.
       new TwigFunction('include', [self::class, 'include'], [
@@ -79,8 +79,8 @@ class TwigExtension extends AbstractExtension {
    */
   public function getFilters() {
     return [
-      new TwigFilter('add_class', [$this, 'addClass']),
-      new TwigFilter('set_attribute', [$this, 'setAttribute']),
+      new TwigFilter('add_class', $this->addClass(...)),
+      new TwigFilter('set_attribute', $this->setAttribute(...)),
     ];
   }
 
diff --git a/tests/modules/ui_patterns_test/src/Hook/UiPatternsTestTokensHooks.php b/tests/modules/ui_patterns_test/src/Hook/UiPatternsTestTokensHooks.php
new file mode 100644
index 0000000..9ab86a7
--- /dev/null
+++ b/tests/modules/ui_patterns_test/src/Hook/UiPatternsTestTokensHooks.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\ui_patterns_test\Hook;
+
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for ui_patterns_test.
+ */
+class UiPatternsTestTokensHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_token_info().
+   */
+  #[Hook('token_info')]
+  public function tokenInfo(): array {
+    $type = [
+      'name' => $this->t('Node Tests'),
+      'description' => $this->t('Tokens related to individual content items, or "nodes".'),
+      'needs-data' => 'entity_test',
+    ];
+    // Core tokens for nodes.
+    $entity['id'] = [
+      'name' => $this->t('Entity ID'),
+      'description' => $this->t('The unique ID of the content item, or "entity_test".'),
+    ];
+    return [
+      'types' => [
+        'node' => $type,
+      ],
+      'tokens' => [
+        'node' => $entity,
+      ],
+    ];
+  }
+
+  /**
+   * Implements hook_tokens().
+   *
+   * @SuppressWarnings("PHPMD.UnusedFormalParameter")
+   */
+  #[Hook('tokens')]
+  public function tokens(string $type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata): array {
+    $replacements = [];
+    if ($type === 'node' && !empty($data['node'])) {
+      $entity = $data['node'];
+      foreach ($tokens as $name => $original) {
+        switch ($name) {
+          // Simple key values on the node.
+          case 'id':
+            $replacements[$original] = $entity->id();
+            break;
+
+          case 'name':
+            $replacements[$original] = $entity->name();
+            break;
+        }
+      }
+    }
+    return $replacements;
+  }
+
+}
diff --git a/tests/modules/ui_patterns_test/ui_patterns_test.services.yml b/tests/modules/ui_patterns_test/ui_patterns_test.services.yml
index bc6e57e..aa4fb3b 100644
--- a/tests/modules/ui_patterns_test/ui_patterns_test.services.yml
+++ b/tests/modules/ui_patterns_test/ui_patterns_test.services.yml
@@ -3,3 +3,7 @@ services:
     class: Drupal\ui_patterns_test\Resolver\TestContextEntityResolver
     tags:
       - { name: ui_patterns.context_entity_resolver }
+
+  Drupal\ui_patterns_test\Hook\UiPatternsTestTokensHooks:
+    class: Drupal\ui_patterns_test\Hook\UiPatternsTestTokensHooks
+    autowire: true
diff --git a/tests/modules/ui_patterns_test/ui_patterns_test.tokens.inc b/tests/modules/ui_patterns_test/ui_patterns_test.tokens.inc
index 0592185..b4ea151 100644
--- a/tests/modules/ui_patterns_test/ui_patterns_test.tokens.inc
+++ b/tests/modules/ui_patterns_test/ui_patterns_test.tokens.inc
@@ -7,28 +7,16 @@
 
 declare(strict_types=1);
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\ui_patterns_test\Hook\UiPatternsTestTokensHooks;
 use Drupal\Core\Render\BubbleableMetadata;
 
 /**
  * Implements hook_token_info().
  */
+#[LegacyHook]
 function ui_patterns_test_token_info(): array {
-  $type = [
-    'name' => t('Node Tests'),
-    'description' => t('Tokens related to individual content items, or "nodes".'),
-    'needs-data' => 'entity_test',
-  ];
-
-  // Core tokens for nodes.
-  $entity['id'] = [
-    'name' => t('Entity ID'),
-    'description' => t('The unique ID of the content item, or "entity_test".'),
-  ];
-
-  return [
-    'types' => ['node' => $type],
-    'tokens' => ['node' => $entity],
-  ];
+  return \Drupal::service(UiPatternsTestTokensHooks::class)->tokenInfo();
 }
 
 /**
@@ -36,23 +24,7 @@ function ui_patterns_test_token_info(): array {
  *
  * @SuppressWarnings("PHPMD.UnusedFormalParameter")
  */
+#[LegacyHook]
 function ui_patterns_test_tokens(string $type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata): array {
-  $replacements = [];
-
-  if ($type === 'node' && !empty($data['node'])) {
-    $entity = $data['node'];
-    foreach ($tokens as $name => $original) {
-      switch ($name) {
-        // Simple key values on the node.
-        case 'id':
-          $replacements[$original] = $entity->id();
-          break;
-
-        case 'name':
-          $replacements[$original] = $entity->name();
-          break;
-      }
-    }
-  }
-  return $replacements;
+  return \Drupal::service(UiPatternsTestTokensHooks::class)->tokens($type, $tokens, $data, $options, $bubbleable_metadata);
 }
diff --git a/tests/src/Kernel/SourcePluginManagerTest.php b/tests/src/Kernel/SourcePluginManagerTest.php
index b122163..633423a 100644
--- a/tests/src/Kernel/SourcePluginManagerTest.php
+++ b/tests/src/Kernel/SourcePluginManagerTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\ui_patterns\Kernel;
 
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\entity_test\EntityTestHelper;
 use Drupal\Core\Plugin\Context\EntityContext;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\KernelTests\KernelTestBase;
@@ -46,7 +48,7 @@ final class SourcePluginManagerTest extends KernelTestBase {
   protected function setUp(): void {
     parent::setUp();
     // @phpstan-ignore-next-line
-    entity_test_create_bundle('ui_patterns');
+    DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => EntityTestHelper::createBundle('ui_patterns'), fn() => entity_test_create_bundle('ui_patterns'));
     $this->installEntitySchema('entity_test');
     $this->installEntitySchema('user');
     $this->sourcePluginManager = \Drupal::service('plugin.manager.ui_patterns_source');
diff --git a/tests/src/Kernel/SourcePluginsTestBase.php b/tests/src/Kernel/SourcePluginsTestBase.php
index 923e399..2e91050 100644
--- a/tests/src/Kernel/SourcePluginsTestBase.php
+++ b/tests/src/Kernel/SourcePluginsTestBase.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\ui_patterns\Kernel;
 
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Core\Plugin\Context\EntityContext;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\Tests\ui_patterns\Traits\ConfigImporterTrait;
@@ -13,6 +14,7 @@ use Drupal\Tests\ui_patterns\Traits\TestContentCreationTrait;
 /**
  * Base class to test source plugins.
  */
+#[RunTestsInSeparateProcesses]
 class SourcePluginsTestBase extends KernelTestBase {
 
   use RunSourcePluginTestTrait;
diff --git a/tests/themes/ui_patterns_test_theme/ui_patterns_test_theme.info.yml b/tests/themes/ui_patterns_test_theme/ui_patterns_test_theme.info.yml
index 311ccaa..8d5eb32 100644
--- a/tests/themes/ui_patterns_test_theme/ui_patterns_test_theme.info.yml
+++ b/tests/themes/ui_patterns_test_theme/ui_patterns_test_theme.info.yml
@@ -2,4 +2,4 @@ name: ui_patterns_test_theme
 type: theme
 base theme: stable9
 description: Theme for testing ui_patterns
-core_version_requirement: ^10.3 || ^11
+core_version_requirement: ^10.3 || ^11 || ^12
diff --git a/ui_patterns.info.yml b/ui_patterns.info.yml
index 15355b7..55f4888 100644
--- a/ui_patterns.info.yml
+++ b/ui_patterns.info.yml
@@ -1,5 +1,5 @@
 name: "UI Patterns"
 type: module
 description: "Define and expose self-contained UI Components as Drupal plugins and use them seamlessly in Drupal development and site-building."
-core_version_requirement: ^10.3.4 || ^11
+core_version_requirement: ^10.3.4 || ^11 || ^12
 package: "User interface"
diff --git a/ui_patterns.module b/ui_patterns.module
index f2964cd..1c6a1b4 100644
--- a/ui_patterns.module
+++ b/ui_patterns.module
@@ -7,20 +7,16 @@
 
 declare(strict_types=1);
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\ui_patterns\Hook\UiPatternsHooks;
 use Drupal\Core\Field\FieldConfigInterface;
-use Drupal\ui_patterns\Plugin\UiPatterns\Source\WysiwygWidget;
 
 /**
  * Implements hook_element_info_alter().
  */
+#[LegacyHook]
 function ui_patterns_element_info_alter(array &$types): void {
-  if (isset($types['component'])) {
-    array_unshift($types['component']['#pre_render'], 'ui_patterns.component_element_alter:alter');
-    array_unshift($types['component']['#pre_render'], 'ui_patterns.component_element_builder:build');
-  }
-  if (isset($types['text_format'])) {
-    $types['text_format']['#pre_render'][] = [WysiwygWidget::class, 'textFormat'];
-  }
+  \Drupal::service(UiPatternsHooks::class)->elementInfoAlter($types);
 }
 
 /**
@@ -30,46 +26,15 @@ function ui_patterns_element_info_alter(array &$types): void {
  *
  * Implements hook_plugin_filter_TYPE__CONSUMER_alter().
  */
+#[LegacyHook]
 function ui_patterns_plugin_filter_block__ui_patterns_alter(array &$definitions, array $extra): void {
-  // We are not allowing 'inline_block' blocks to avoid dependencies from config
-  // entities to content entities.
-  $definitions = array_filter($definitions, static function ($definition) {
-    return $definition['id'] !== 'inline_block';
-  });
-  // Those blocks are not allowed in layout builder. Let's do the same.
-  unset($definitions['system_main_block'], $definitions['page_title_block']);
-
-  // Add a boolean marker '_ui_patterns_compatible' to all remaining definitions
-  // Other modules can use the same hook to modify this value.
-  // This allows to add or remove blocks.
-  $forbidden_blocks = [
-    'provider' => ['layout_builder', 'ui_patterns_blocks'],
-  ];
-  foreach ($definitions as $id => &$definition) {
-    if (isset($definitions[$id]['_ui_patterns_compatible'])) {
-      // When a block plugin already has '_ui_patterns_compatible'
-      // It probably means it has been marked by another code.
-      // Honor what the other code has done and do not override.
-      continue;
-    }
-    $compatibilityFlag = TRUE;
-    if (in_array($definition['provider'], $forbidden_blocks['provider'], TRUE)) {
-      $compatibilityFlag = FALSE;
-    }
-    // Filter out blocks with _block_ui_hidden ?
-    $definitions[$id]['_ui_patterns_compatible'] = $compatibilityFlag;
-  }
+  \Drupal::service(UiPatternsHooks::class)->pluginFilterBlockUiPatternsAlter($definitions, $extra);
 }
 
 /**
  * Implements hook_ENTITY_TYPE_delete().
  */
+#[LegacyHook]
 function ui_patterns_field_config_delete(FieldConfigInterface $field_config): void {
-  $sample_entity_generator = Drupal::service('ui_patterns.sample_entity_generator');
-  $entity_type = $field_config->getTargetEntityTypeId();
-  $bundle = $field_config->getTargetBundle();
-  $sample_entity_generator->delete($entity_type, $bundle);
-  // @todo trigger an event and do this logic in UiPatternsEntitySchemaSubscriber
-  Drupal::service('plugin.manager.ui_patterns_source')->clearCachedDefinitions();
-  Drupal::service('plugin.manager.ui_patterns_derivable_context')->clearCachedDefinitions();
+  \Drupal::service(UiPatternsHooks::class)->fieldConfigDelete($field_config);
 }
diff --git a/ui_patterns.services.yml b/ui_patterns.services.yml
index 182cee0..cfdb322 100644
--- a/ui_patterns.services.yml
+++ b/ui_patterns.services.yml
@@ -102,3 +102,7 @@ services:
   ui_patterns.normalizer:
     class: Drupal\ui_patterns\UiPatternsNormalizer
     arguments: ["@renderer"]
+
+  Drupal\ui_patterns\Hook\UiPatternsHooks:
+    class: Drupal\ui_patterns\Hook\UiPatternsHooks
+    autowire: true
