diff --git a/app/assets/javascripts/entrypoints/graphql_explorer.js b/app/assets/javascripts/entrypoints/graphql_explorer.js
index 801b3c8056a0e5ebcfebcc49422f5b713150a557..6a5b8ddea671b4cf8abf75a317b87b07016e9c58 100644
--- a/app/assets/javascripts/entrypoints/graphql_explorer.js
+++ b/app/assets/javascripts/entrypoints/graphql_explorer.js
@@ -1,14 +1,16 @@
 import '~/webpack';
-
 import gitlabLogo from '@gitlab/svgs/dist/illustrations/gitlab_logo.svg?raw';
 import { gql } from '@apollo/client';
 import { GraphiQL } from 'graphiql';
 /* eslint-disable no-restricted-imports */
 import React from 'react';
 import { createRoot } from 'react-dom/client';
+import { Mousetrap } from '~/lib/mousetrap';
 import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
 /* eslint-enable no-restricted-imports */
 import createDefaultClient, { fetchPolicies } from '~/lib/graphql';
+import { keysFor, TOGGLE_PERFORMANCE_BAR } from '~/behaviors/shortcuts/keybindings';
+import Shortcuts from '~/behaviors/shortcuts/shortcuts';
 
 const apolloClient = createDefaultClient(
   {},
@@ -107,3 +109,5 @@ function apolloFetcher(graphQLParams, { headers }) {
 createRoot(graphiqlContainer).render(
   React.createElement(GraphiQL, { defaultQuery, fetcher: apolloFetcher }, GraphiQLLogo),
 );
+
+Mousetrap.bind(keysFor(TOGGLE_PERFORMANCE_BAR), Shortcuts.onTogglePerfBar);
diff --git a/app/assets/javascripts/entrypoints/performance_bar.js b/app/assets/javascripts/entrypoints/performance_bar.js
index d1be41cbed10093677de21cf61db6e94cb24c41e..de7dce8f697a48adbd9620688e90853a75389063 100644
--- a/app/assets/javascripts/entrypoints/performance_bar.js
+++ b/app/assets/javascripts/entrypoints/performance_bar.js
@@ -1,3 +1,5 @@
 import '~/webpack';
 import '~/commons';
-import '~/performance_bar';
+import initPerformanceBarAndLog from '~/performance_bar';
+
+initPerformanceBarAndLog();
diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
index c62c3b5e17d59ccbcd5f6289b06fa11199b5dbf3..00acc3d263c8321da169cdc127e4f7502e2032e8 100644
--- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
@@ -166,6 +166,7 @@ export default {
       :title="header"
       size="lg"
       scrollable
+      :static="true"
       hide-backdrop
       hide-footer
       no-focus-on-show
diff --git a/app/assets/javascripts/performance_bar/components/info_modal/info_app.vue b/app/assets/javascripts/performance_bar/components/info_modal/info_app.vue
index a16474bbc5002df7dd7c2f4de12f6bd2874216f0..ecdabda93f3c24fd2afcfc470c02e599d0b2af95 100644
--- a/app/assets/javascripts/performance_bar/components/info_modal/info_app.vue
+++ b/app/assets/javascripts/performance_bar/components/info_modal/info_app.vue
@@ -51,6 +51,7 @@ export default {
       modal-id="environment-info"
       :title="s__('PerformanceBar|Debugging information')"
       size="sm"
+      :static="true"
       hide-backdrop
       hide-footer
       no-focus-on-show
diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js
index 77047e126683f15705958acbef668a1e49f005a7..11f9c2ee75360625256fb20e1b2022c4b78c0e25 100644
--- a/app/assets/javascripts/performance_bar/index.js
+++ b/app/assets/javascripts/performance_bar/index.js
@@ -5,6 +5,7 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
 import { s__ } from '~/locale';
 import Translate from '~/vue_shared/translate';
 
+import { transformRootToHostRules } from '~/performance_bar/transform_root_to_host_rules';
 import initPerformanceBarLog from './performance_bar_log';
 import PerformanceBarService from './services/performance_bar_service';
 import PerformanceBarStore from './stores/performance_bar_store';
@@ -163,7 +164,18 @@ const initPerformanceBar = (el) => {
   });
 };
 
-initPerformanceBar(document.querySelector('#js-peek'));
-initPerformanceBarLog();
+export function getPerformanceBarNode() {
+  const shadowDomRoot = document.querySelector('#performance-bar-root')?.shadowRoot;
+  if (shadowDomRoot) {
+    transformRootToHostRules(shadowDomRoot);
+    return shadowDomRoot.querySelector('#js-peek');
+  }
+  return document.querySelector('#js-peek');
+}
+
+export default function initPerformanceBarAndLog() {
+  const app = initPerformanceBar(getPerformanceBarNode());
+  initPerformanceBarLog();
 
-export default initPerformanceBar;
+  return app;
+}
diff --git a/app/assets/javascripts/performance_bar/transform_root_to_host_rules.js b/app/assets/javascripts/performance_bar/transform_root_to_host_rules.js
new file mode 100644
index 0000000000000000000000000000000000000000..20863001e0995b44c492e167a25fc6d65bcb3c4d
--- /dev/null
+++ b/app/assets/javascripts/performance_bar/transform_root_to_host_rules.js
@@ -0,0 +1,19 @@
+export function transformRootToHostRules(shadowDomRoot) {
+  if (!shadowDomRoot?.styleSheets?.length) {
+    return;
+  }
+  let cssText = '';
+
+  for (const sheet of shadowDomRoot.styleSheets) {
+    for (const rule of sheet.cssRules) {
+      if (rule instanceof CSSStyleRule && [':root', 'body'].includes(rule.selectorText)) {
+        cssText += `:host {${rule.style.cssText}}\n`;
+      }
+    }
+  }
+
+  const newStyleSheet = new CSSStyleSheet();
+  newStyleSheet.replaceSync(cssText);
+
+  shadowDomRoot.adoptedStyleSheets.push(newStyleSheet);
+}
diff --git a/app/assets/stylesheets/page_bundles/graphql_explorer.scss b/app/assets/stylesheets/page_bundles/graphql_explorer.scss
index fd9e259b9c8d5d44094cb42dbe1ed6d4acf65213..56b47c19e1e9fbd99c4dca8cccf610138e2b39d2 100644
--- a/app/assets/stylesheets/page_bundles/graphql_explorer.scss
+++ b/app/assets/stylesheets/page_bundles/graphql_explorer.scss
@@ -1,4 +1,5 @@
 @use 'graphiql/graphiql.css';
+@use 'framework/variables';
 
 html, body, #graphiql-container {
   height: 100%;
@@ -7,6 +8,11 @@ html, body, #graphiql-container {
   width: 100%;
 }
 
+.with-performance-bar #graphiql-container {
+  padding-top: variables.$performance-bar-height;
+  height: calc(100% - variables.$performance-bar-height);
+}
+
 .graphiql-container .graphiql-logo {
   height: 24px;
   padding: 0 15px;
diff --git a/app/controllers/api/graphql/graphql_explorer_controller.rb b/app/controllers/api/graphql/graphql_explorer_controller.rb
index 8e873cd85d7d0642f2821852c9fe0fa7074a9205..5f12f07043bffef9ee48c41f4205ec0d9fef2e17 100644
--- a/app/controllers/api/graphql/graphql_explorer_controller.rb
+++ b/app/controllers/api/graphql/graphql_explorer_controller.rb
@@ -4,6 +4,7 @@ module API
   module Graphql
     class GraphqlExplorerController < BaseActionController
       include Gitlab::GonHelper
+      include WithPerformanceBar
 
       def show
         # We need gon to setup gon.relative_url_root which is used by our Apollo client
diff --git a/app/views/api/graphql/graphql_explorer/show.html.haml b/app/views/api/graphql/graphql_explorer/show.html.haml
index cb02422f993f2b4fd48cf300afbf9c51e8439482..71241bfdc973d2e931ee151e3046b20bca3907a3 100644
--- a/app/views/api/graphql/graphql_explorer/show.html.haml
+++ b/app/views/api/graphql/graphql_explorer/show.html.haml
@@ -9,5 +9,14 @@
 
     = universal_stylesheet_link_tag 'page_bundles/graphql_explorer'
     = webpack_bundle_tag 'graphql_explorer'
-  %body
+  %body{ class: ('with-performance-bar' if performance_bar_enabled?) }
+    - if performance_bar_enabled?
+      #performance-bar-root
+        %template{ :shadowrootmode => "open" }
+          = render 'peek/bar'
+          = universal_stylesheet_link_tag 'application'
+          = universal_stylesheet_link_tag 'application_utilities'
+          = universal_stylesheet_link_tag 'tailwind'
+          = universal_stylesheet_link_tag 'performance_bar'
+          = webpack_bundle_tag 'performance_bar'
     #graphiql-container Loading...
diff --git a/spec/frontend/performance_bar/index_spec.js b/spec/frontend/performance_bar/index_spec.js
index cfc752655bd1a425db8e5e58fdfc70615869c6fe..43abd05b9bba4a981006bac2ddd75a11f6c4677c 100644
--- a/spec/frontend/performance_bar/index_spec.js
+++ b/spec/frontend/performance_bar/index_spec.js
@@ -1,23 +1,35 @@
-import Vue from 'vue';
 import MockAdapter from 'axios-mock-adapter';
-import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { resetHTMLFixture, setHTMLFixture } from 'helpers/fixtures';
 import axios from '~/lib/utils/axios_utils';
 import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import '~/performance_bar/components/performance_bar_app.vue';
-import performanceBar from '~/performance_bar';
+import initPerformanceBarAndLog from '~/performance_bar';
 import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
 
-Vue.config.ignoredElements = ['gl-emoji'];
-
 jest.mock('~/performance_bar/performance_bar_log');
 
-describe('performance bar wrapper', () => {
+function setupNormalDOM() {
+  setHTMLFixture('<div id="js-peek"></div>');
+  return document.getElementById('js-peek');
+}
+
+function setupShadowDOM() {
+  setHTMLFixture(`<div id="performance-bar-root"></div>`);
+  const host = document.querySelector('#performance-bar-root');
+  const shadow = host.attachShadow({ mode: 'open' });
+  const peekWrapper = document.createElement('div');
+  shadow.appendChild(peekWrapper);
+  return peekWrapper;
+}
+
+describe.each([
+  ['normal DOM', setupNormalDOM],
+  ['shadow DOM', setupShadowDOM],
+])('Performance Bar – Using %s', (_, setupFn) => {
   let mock;
   let vm;
 
   beforeEach(() => {
-    setHTMLFixture('<div id="js-peek"></div>');
-    const peekWrapper = document.getElementById('js-peek');
+    const peekWrapper = setupFn();
     performance.getEntriesByType = jest.fn().mockReturnValue([]);
 
     peekWrapper.setAttribute('id', 'js-peek');
@@ -47,12 +59,13 @@ describe('performance bar wrapper', () => {
       {},
     );
 
-    vm = performanceBar(peekWrapper);
+    vm = initPerformanceBarAndLog();
   });
 
   afterEach(() => {
     vm.$destroy();
-    document.getElementById('js-peek').remove();
+    document?.getElementById('performance-bar-root')?.remove?.();
+    document?.getElementById('js-peek')?.remove?.();
     mock.restore();
     resetHTMLFixture();
   });