diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 687609d706bec3df44b7bb1948bc88cf1fb3318a..3d44375b4b365f0707e643a6a52ef9536e4f7361 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -32,6 +32,48 @@
   text-align: right;
 }
 
+.frontend-editing-toolbar-toggle {
+  background: rgba(var(--fe-editing-primary-color), 0.6);
+  padding: 0.5rem 0.75rem;
+  text-transform: uppercase;
+  color: white;
+  display: block;
+  font-weight: 500;
+  border-radius: 999px;
+  box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
+  text-align: right;
+  height: 1.9rem;
+  margin-right: 1rem;
+  min-width: 5rem;
+}
+
+.frontend-editing-toolbar-toggle a {
+  color: white;
+}
+
+.gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-tab.frontend-editing-toolbar-toggle:hover {
+  border-radius: 999px;
+}
+
+.gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-tab.frontend-editing-toolbar-toggle:focus-within {
+  border-radius: 999px;
+}
+
+.gin-secondary-toolbar__layout-container a.frontend-editing-toggle-link:focus {
+  box-shadow: none;
+}
+
+a.frontend-editing-toggle-link,
+.toolbar-tab a.frontend-editing-toggle-link:focus:not(:hover) {
+  text-decoration: none;
+}
+
+.frontend-editing-toggle-link:hover:focus,
+.frontend-editing-toggle-link:hover {
+  color: white;
+  text-decoration: underline;
+}
+
 .frontend-editing-toggle a::before {
   content: ' ';
   position: absolute;
@@ -50,7 +92,26 @@
   border-radius: 999px;
 }
 
-.frontend-editing-toggle a.frontend-editing--enabled {
+.frontend-editing-toolbar-toggle a::before {
+  content: ' ';
+  position: absolute;
+  top: 0;
+  left: 0.5rem;
+  bottom: 0;
+  width: 1.5rem;
+  height: 1.5rem;
+  margin-bottom: auto;
+  margin-top: auto;
+  background-color: #fff;
+  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
+  background-size: 14px;
+  background-repeat: no-repeat;
+  background-position: center;
+  border-radius: 100%;
+}
+
+.frontend-editing-toggle a.frontend-editing--enabled,
+.frontend-editing-toolbar-toggle:has(a.frontend-editing--enabled) {
   background: rgb(var(--fe-editing-primary-color));
   text-align: left;
 }
@@ -59,6 +120,10 @@
   left: calc(100% - 1.75rem - 0.5rem);
 }
 
+.frontend-editing-toolbar-toggle a.frontend-editing--enabled::before {
+  left: calc(100% - 1.75rem - 0.25rem);
+}
+
 .frontend-editing-toggle-not-configured {
   box-shadow: 0 0 0 0 rgba(255, 0, 0, 1);
   transform: scale(1);
@@ -81,3 +146,23 @@
     box-shadow: 0 0 0 0 rgba(255, 0, 0, 0);
   }
 }
+
+@media only screen and (max-width: 975px) {
+  .frontend-editing-toolbar-toggle {
+    margin-top: 0.25rem;
+    margin-right: 0;
+  }
+
+  .frontend-editing-toolbar-toggle a::before {
+    left: auto;
+    right: 2.35rem;
+  }
+
+  .frontend-editing-toolbar-toggle a.frontend-editing--enabled::before {
+    left: calc(100% - 1rem - 0.1rem);
+  }
+
+  a.frontend-editing-toggle-link {
+    position: relative;
+  }
+}
diff --git a/frontend_editing.module b/frontend_editing.module
index 1ee9a3e1be54ff6b9253ebfe349f947bcbdce810..f55ebeebe53405b0045ec6b431a51041e606ba11 100644
--- a/frontend_editing.module
+++ b/frontend_editing.module
@@ -18,6 +18,7 @@ use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FormatterInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Hook\Attribute\LegacyHook;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\Markup;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -25,6 +26,7 @@ use Drupal\Core\Url;
 use Drupal\frontend_editing\Ajax\CloseSidePanelCommand;
 use Drupal\frontend_editing\Ajax\EntityPreviewCommand;
 use Drupal\frontend_editing\Ajax\ScrollTopCommand;
+use Drupal\frontend_editing\Hook\FrontendEditingHooks;
 use Drupal\node\NodeInterface;
 
 /**
@@ -274,6 +276,7 @@ function frontend_editing_field_formatter_third_party_settings_form(FormatterInt
   }
   return $element;
 }
+
 /**
  * Check formatter third party settings.
  */
@@ -289,6 +292,7 @@ function frontend_editing_check_third_party_formatter_settings(&$element) {
     }
   }
 }
+
 /**
  * Implements hook_field_formatter_settings_summary_alter().
  */
@@ -802,3 +806,11 @@ function frontend_editing_preprocess_page_title(&$variables) {
     }
   }
 }
+
+/**
+ * Implements hook_toolbar().
+ */
+#[LegacyHook]
+function frontend_editing_toolbar() {
+  return \Drupal::service(FrontendEditingHooks::class)->toolbar();
+}
diff --git a/frontend_editing.services.yml b/frontend_editing.services.yml
index 696c3d4aafa0641a7f655b36cd54f58977cc31f4..159ff0081951f68b1d2ab2e80730adb92f7f4646 100644
--- a/frontend_editing.services.yml
+++ b/frontend_editing.services.yml
@@ -9,3 +9,10 @@ services:
   frontend_editing.form_builder:
     class: Drupal\frontend_editing\FrontendEditingFormBuilder
     arguments: ['@entity_type.manager', '@entity.form_builder', '@form_builder']
+  frontend_editing.toolbar_item:
+    class: Drupal\frontend_editing\ToolbarItem
+    arguments: [ '@plugin.manager.element_info', '@user.data', '@current_user' ]
+  Drupal\frontend_editing\ToolbarItem: '@frontend_editing.toolbar_item'
+  Drupal\frontend_editing\Hook\FrontendEditingHooks:
+    class: Drupal\frontend_editing\Hook\FrontendEditingHooks
+    autowire: true
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index 6a76076a2aeac2ffb8022e005f0640e291576a63..b447d9f9d8afef36fa76cb9c962cd2e33243248e 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -11,7 +11,6 @@ use Drupal\Core\Ajax\InvokeCommand;
 use Drupal\Core\Ajax\MessageCommand;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\EntityFormBuilder;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\RendererInterface;
@@ -79,6 +78,8 @@ class FrontendEditingController extends ControllerBase {
    *   The user data storage.
    * @param \Drupal\frontend_editing\FieldReferenceHelperInterface $field_reference_helper
    *   The field reference helper.
+   * @param \Drupal\frontend_editing\FrontendEditingFormBuilderInterface $frontend_editing_form_builder
+   *   The frontend editing form builder.
    */
   public function __construct(RendererInterface $renderer, EntityRepositoryInterface $entity_repository, UserDataInterface $userData, FieldReferenceHelperInterface $field_reference_helper, FrontendEditingFormBuilderInterface $frontend_editing_form_builder) {
     $this->renderer = $renderer;
@@ -121,22 +122,22 @@ class FrontendEditingController extends ControllerBase {
     $response = new AjaxResponse();
     if ($new_state) {
       $message = $this->t('Frontend editing has been enabled.');
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'addClass', ['frontend-editing--enabled']));
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'text', [$this->t('On')]));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'addClass', ['frontend-editing--enabled']));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'text', [$this->t('On')]));
       $response->addCommand(new InvokeCommand('body', 'removeClass', ['frontend-editing--hidden']));
     }
     else {
       $message = $this->t('Frontend editing has been disabled.');
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing--enabled']));
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'text', [$this->t('Off')]));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'removeClass', ['frontend-editing--enabled']));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'text', [$this->t('Off')]));
       $response->addCommand(new InvokeCommand('body', 'addClass', ['frontend-editing--hidden']));
     }
     $response->addCommand(new MessageCommand($message, NULL, ['type' => 'status']));
-    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'attr', [
+    $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'attr', [
       'data-toggle-state',
       $new_state,
     ]));
-    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
+    $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
 
     return $response;
   }
diff --git a/src/Controller/PreviewController.php b/src/Controller/PreviewController.php
index 41977a855132a91fd7e9ba70c648cf00f0feaa66..02423c42c2fe7a9d0404923d54744a300fa03b69 100644
--- a/src/Controller/PreviewController.php
+++ b/src/Controller/PreviewController.php
@@ -34,7 +34,7 @@ class PreviewController extends PreviewControllerBase {
   /**
    * {@inheritdoc}
    */
-  public function view(EntityInterface $entity_preview, $view_mode_id = 'default', $langcode = NULL, Request $request = NULL) {
+  public function view(EntityInterface $entity_preview, $view_mode_id = 'default', $langcode = NULL, ?Request $request = NULL) {
     $build = parent::view($entity_preview, $view_mode_id, $langcode);
     // In case it is ajax request, respond with ajax response.
     if ($request->isXmlHttpRequest()) {
@@ -52,7 +52,7 @@ class PreviewController extends PreviewControllerBase {
   /**
    * {@inheritdoc}
    */
-  public function nodeView(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL, Request $request = NULL) {
+  public function nodeView(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL, ?Request $request = NULL) {
     $build = parent::view($node_preview, $view_mode_id, $langcode);
     // In case it is ajax request, respond with ajax response.
     if ($request->isXmlHttpRequest()) {
diff --git a/src/Form/EntityReferenceAddForm.php b/src/Form/EntityReferenceAddForm.php
index 8a12b26d0829d3dff3404f75c6d3a008cbdeef09..ea7cbf208ab0c338113da9279fe0559f17f6d2d6 100644
--- a/src/Form/EntityReferenceAddForm.php
+++ b/src/Form/EntityReferenceAddForm.php
@@ -48,7 +48,7 @@ class EntityReferenceAddForm extends FormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, FieldDefinitionInterface $field_definition = NULL) {
+  public function buildForm(array $form, FormStateInterface $form_state, ?FieldDefinitionInterface $field_definition = NULL) {
     if (!empty($field_definition)) {
       $settings = $field_definition->getSettings();
       if ($settings['target_type'] == 'media' && $this->moduleHandler->moduleExists('media_library_form_element')) {
diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
new file mode 100644
index 0000000000000000000000000000000000000000..c9915594a7c8924184d9f57f5bca263afa6ea21b
--- /dev/null
+++ b/src/Hook/FrontendEditingHooks.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Drupal\frontend_editing\Hook;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Render\ElementInfoManager;
+use Drupal\Core\Routing\AdminContext;
+use Drupal\Core\Session\AccountProxyInterface;
+use Drupal\frontend_editing\ToolbarItem;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
+
+/**
+ * Hook implementations for frontend_editing.
+ */
+class FrontendEditingHooks {
+
+  /**
+   * Constructs FrontendEditing hooks.
+   *
+   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
+   *   The current user.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
+   *   The config factory object.
+   * @param \Drupal\Core\Routing\AdminContext $adminContext
+   *   The admin context.
+   * @param \Drupal\Core\Render\ElementInfoManager $elementInfoManager
+   *   The element info manager.
+   */
+  public function __construct(
+    #[Autowire(service: 'current_user')]
+    protected AccountProxyInterface $currentUser,
+    #[Autowire(service: 'config.factory')]
+    protected ConfigFactoryInterface $configFactory,
+    #[Autowire(service: 'router.admin_context')]
+    protected AdminContext $adminContext,
+    #[Autowire(service: 'plugin.manager.element_info')]
+    protected ElementInfoManager $elementInfoManager,
+  ) {}
+
+  /**
+   * Implements hook_toolbar().
+   */
+  #[Hook('toolbar')]
+  public function toolbar() {
+    $items = [];
+    $cache_metadata = new CacheableMetadata();
+    $cache_metadata->addCacheContexts(['user.permissions']);
+    if (!$this->currentUser->hasPermission('access frontend editing')) {
+      $cache_metadata->applyTo($items);
+      return $items;
+    }
+
+    $config = $this->configFactory->get('frontend_editing.settings');
+    $cache_metadata->addCacheableDependency($config);
+    $cache_metadata->addCacheContexts(['route']);
+    $ui_toggle_enabled = $config->get('ui_toggle');
+    if ($ui_toggle_enabled || $this->adminContext->isAdminRoute()) {
+      $cache_metadata->applyTo($items);
+      return $items;
+    }
+
+    $items['toggle'] = [
+      '#type' => 'toolbar_item',
+      'tab' => [
+        '#lazy_builder' => [
+          'frontend_editing.toolbar_item:renderToggle',
+          [],
+        ],
+        '#create_placeholder' => TRUE,
+        '#cache' => [
+          'tags' => [
+            'frontend_editing:toggle',
+          ],
+        ],
+      ],
+      '#wrapper_attributes' => [
+        'class' => [
+          'frontend-editing-toolbar-toggle',
+        ],
+      ],
+      '#weight' => -11,
+    ];
+    // \Drupal\toolbar\Element\ToolbarItem::preRenderToolbarItem adds an
+    // #attributes property to each toolbar item's tab child automatically.
+    // Lazy builders don't support an #attributes property so we need to
+    // add another render callback to remove the #attributes property. We start
+    // by adding the defaults, and then we append our own pre render callback.
+    $items['toggle'] += $this->elementInfoManager->getInfo('toolbar_item');
+    $items['toggle']['#pre_render'][] = [ToolbarItem::class, 'removeTabAttributes'];
+    $cache_metadata->applyTo($items);
+    return $items;
+  }
+
+}
diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
new file mode 100644
index 0000000000000000000000000000000000000000..e9320ab6ca02c1e4d80b37a369cd870c5d607220
--- /dev/null
+++ b/src/ToolbarItem.php
@@ -0,0 +1,100 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\frontend_editing;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Render\ElementInfoManagerInterface;
+use Drupal\Core\Security\TrustedCallbackInterface;
+use Drupal\Core\Session\AccountProxyInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Url;
+use Drupal\user\UserDataInterface;
+
+/**
+ * Defines a class for lazy building render arrays.
+ *
+ * @internal
+ */
+final class ToolbarItem implements TrustedCallbackInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * Constructs LazyBuilders object.
+   *
+   * @param \Drupal\Core\Render\ElementInfoManagerInterface $elementInfo
+   *   Element info.
+   * @param \Drupal\user\UserDataInterface $userData
+   *   The user data.
+   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
+   *   The current user.
+   */
+  public function __construct(
+    protected ElementInfoManagerInterface $elementInfo,
+    protected UserDataInterface $userData,
+    protected AccountProxyInterface $currentUser,
+  ) {}
+
+  /**
+   * Render announcements.
+   *
+   * @return array
+   *   Render array.
+   */
+  public function renderToggle(): array {
+    $toggle_state = $this->userData->get(
+      'frontend_editing',
+      $this->currentUser->id(),
+      'enabled'
+    );
+    $active_class = $toggle_state ? 'frontend-editing--enabled' : '';
+
+    $build = [
+      '#type' => 'link',
+      '#url' => Url::fromRoute('frontend_editing.toggle'),
+      '#cache' => [
+        'context' => ['user.permissions'],
+      ],
+      '#title' => $toggle_state ? $this->t('On') : $this->t('Off'),
+      '#id' => Html::getId('frontend-editing-toolbar-toggle-link'),
+      '#attributes' => [
+        'title' => $this->t('Enable frontend editing actions'),
+        'class' => [
+          'use-ajax',
+          'frontend-editing-toggle-link',
+          $active_class,
+        ],
+      ],
+      '#attached' => [
+        'library' => [
+          'core/drupal.ajax',
+          'frontend_editing/ui_toggle',
+        ],
+      ],
+    ];
+
+    // The renderer has already added element defaults by the time the lazy
+    // builder is run.
+    // @see https://www.drupal.org/project/drupal/issues/2609250
+    $build += $this->elementInfo->getInfo('link');
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function trustedCallbacks(): array {
+    return ['renderToggle', 'removeTabAttributes'];
+  }
+
+  /**
+   * Render callback.
+   */
+  public static function removeTabAttributes(array $element): array {
+    unset($element['tab']['#attributes']);
+    return $element;
+  }
+
+}