diff --git a/core/modules/views/src/Attribute/ViewsDisplayExtender.php b/core/modules/views/src/Attribute/ViewsDisplayExtender.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c1ed7020fa1882f656e35353f78895f05ce10e6
--- /dev/null
+++ b/core/modules/views/src/Attribute/ViewsDisplayExtender.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\views\Attribute;
+
+use Drupal\Component\Plugin\Attribute\Plugin;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Defines a Plugin attribute object for views display extender plugins.
+ *
+ * @see \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase
+ *
+ * @ingroup views_display_extender_plugins
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class ViewsDisplayExtender extends Plugin {
+
+  /**
+   * Constructs an ViewsDisplayExtender attribute.
+   *
+   * @param string $id
+   *   The plugin ID.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $title
+   *   The plugin title used in the views UI.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $short_title
+   *   (optional) The short title used in the views UI.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $help
+   *   A short help string; this is displayed in the views UI.
+   * @param bool $no_ui
+   *   Whether the plugin is selectable in the UI.
+   * @param class-string|null $deriver
+   *   (optional) The deriver class.
+   */
+  public function __construct(
+    public readonly string $id,
+    public readonly ?TranslatableMarkup $title = NULL,
+    public readonly ?TranslatableMarkup $short_title = NULL,
+    public readonly ?TranslatableMarkup $help = NULL,
+    public readonly bool $no_ui = FALSE,
+    public readonly ?string $deriver = NULL
+  ) {}
+
+}
diff --git a/core/modules/views/src/Plugin/ViewsPluginManager.php b/core/modules/views/src/Plugin/ViewsPluginManager.php
index 8124d316302887335465180ed80406de6a58ebf2..f800b0611db6521cb446bcd4ce19ba27c94591fc 100644
--- a/core/modules/views/src/Plugin/ViewsPluginManager.php
+++ b/core/modules/views/src/Plugin/ViewsPluginManager.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\views\Plugin;
 
+use Drupal\Component\Plugin\Attribute\Plugin;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
@@ -21,7 +22,7 @@ class ViewsPluginManager extends DefaultPluginManager {
    *   The plugin type, for example filter.
    * @param \Traversable $namespaces
    *   An object that implements \Traversable which contains the root paths
-   *   keyed by the corresponding namespace to look for plugin implementations,
+   *   keyed by the corresponding namespace to look for plugin implementations.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
    *   Cache backend instance to use.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -29,7 +30,10 @@ class ViewsPluginManager extends DefaultPluginManager {
    */
   public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
     $plugin_definition_annotation_name = 'Drupal\views\Annotation\Views' . Container::camelize($type);
-    parent::__construct("Plugin/views/$type", $namespaces, $module_handler, 'Drupal\views\Plugin\views\ViewsPluginInterface', $plugin_definition_annotation_name);
+    // Special handling until all views plugins have attribute classes.
+    $attribute_name_candidate = 'Drupal\views\Attribute\Views' . Container::camelize($type);
+    $plugin_definition_attribute_name = class_exists($attribute_name_candidate) ? $attribute_name_candidate : Plugin::class;
+    parent::__construct("Plugin/views/$type", $namespaces, $module_handler, 'Drupal\views\Plugin\views\ViewsPluginInterface', $plugin_definition_attribute_name, $plugin_definition_annotation_name);
 
     $this->defaults += [
       'parent' => 'parent',
diff --git a/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php b/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php
index 719e26131e1e67a626a6f8751948d407efd78130..e9ca9700450d0c497789b0c7c4f990b095d84a0f 100644
--- a/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php
+++ b/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php
@@ -2,18 +2,20 @@
 
 namespace Drupal\views\Plugin\views\display_extender;
 
+use Drupal\views\Attribute\ViewsDisplayExtender;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
 /**
  * Default display extender plugin; does nothing.
  *
  * @ingroup views_display_extender_plugins
- *
- * @ViewsDisplayExtender(
- *   id = "default",
- *   title = @Translation("Empty display extender"),
- *   help = @Translation("Default settings for this view."),
- *   no_ui = TRUE
- * )
  */
+#[ViewsDisplayExtender(
+    id: 'default',
+    title: new TranslatableMarkup('Empty display extender'),
+    help: new TranslatableMarkup('Default settings for this view.'),
+    no_ui: TRUE
+)]
 class DefaultDisplayExtender extends DisplayExtenderPluginBase {
 
 }
diff --git a/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php b/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php
index 979adf4588d7c66fc3c884691a047f6c8e2de80a..1d92b483d65e481b53f1fd8689f629e5515d21fe 100644
--- a/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php
@@ -18,9 +18,8 @@
  *
  * Display extender plugins extend
  * \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase.
- * They must be annotated with
- * \Drupal\views\Annotation\ViewsDisplayExtender annotation, and they
- * must be in namespace directory Plugin\views\display_extender.
+ * They must have \Drupal\views\Attribute\ViewsDisplayExtender attributes, and
+ * they must be in namespace directory Plugin\views\display_extender.
  *
  * @ingroup views_plugins
  *
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest.php
index ff08fa4e933989599e894ea16f307c7a55c5bc4f..c3ba30373430688f7ea7f782ed53e259745a27f3 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest.php
@@ -4,16 +4,17 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsDisplayExtender;
 use Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase;
 
 /**
  * Defines a display extender test plugin.
- *
- * @ViewsDisplayExtender(
- *   id = "display_extender_test",
- *   title = @Translation("Display extender test")
- * )
  */
+#[ViewsDisplayExtender(
+  id: 'display_extender_test',
+  title: new TranslatableMarkup('Display extender test'),
+)]
 class DisplayExtenderTest extends DisplayExtenderPluginBase {
 
   /**
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest2.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest2.php
index 04c156c277ee279d0df33a673f3ae71e619a76ec..0abf74bba9719d4844a0ebb18d3dc5b97f7ae1d3 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest2.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest2.php
@@ -2,14 +2,16 @@
 
 namespace Drupal\views_test_data\Plugin\views\display_extender;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsDisplayExtender;
+
 /**
  * Defines another display extender test plugin.
- *
- * @ViewsDisplayExtender(
- *   id = "display_extender_test_2",
- *   title = @Translation("Display extender test number two")
- * )
  */
+#[ViewsDisplayExtender(
+    id: 'display_extender_test_2',
+    title: new TranslatableMarkup('Display extender test number two'),
+)]
 class DisplayExtenderTest2 extends DisplayExtenderTest {
 
 }
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest3.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest3.php
index 23586af9c21496c6d26269e6ce889c494873bab3..826eda97879f27a8cb286ead8cbc0fec28110596 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest3.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest3.php
@@ -2,14 +2,16 @@
 
 namespace Drupal\views_test_data\Plugin\views\display_extender;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsDisplayExtender;
+
 /**
  * Defines the third display extender test plugin.
- *
- * @ViewsDisplayExtender(
- *   id = "display_extender_test_3",
- *   title = @Translation("Display extender test number three")
- * )
  */
+#[ViewsDisplayExtender(
+  id: 'display_extender_test_3',
+  title: new TranslatableMarkup('Display extender test number three'),
+)]
 class DisplayExtenderTest3 extends DisplayExtenderTest {
 
   /**