From 3ed448fed7f8eb6779ddbdceec832c948e88aa31 Mon Sep 17 00:00:00 2001
From: Peter White <1788320+peterwhite@users.noreply.github.com>
Date: Tue, 6 Oct 2026 14:11:41 +0200
Subject: [PATCH] Keep ?ask out of the search field while integration
assistants load (#4661)
---
.changeset/ask-param-no-search-flash.md | 5 +++++
.../src/components/Embeddable/EmbeddableRootLayout.tsx | 3 +++
.../src/components/Integrations/LoadIntegrations.tsx | 4 ++++
.../src/components/Search/useSearchController.tsx | 10 +++++++---
4 files changed, 19 insertions(+), 3 deletions(-)
create mode 100644 .changeset/ask-param-no-search-flash.md
diff --git a/.changeset/ask-param-no-search-flash.md b/.changeset/ask-param-no-search-flash.md
new file mode 100644
index 000000000..88733a4fa
--- /dev/null
+++ b/.changeset/ask-param-no-search-flash.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Stop the `?ask` query briefly appearing in the search field before an integration assistant opens.
diff --git a/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx b/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx
index 4ac7a73a9..645dd450f 100644
--- a/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx
+++ b/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx
@@ -1,6 +1,7 @@
import type { CustomizationDefaultThemeMode } from '@gitbook/api';
import { SiteInsightsTrademarkPlacement } from '@gitbook/api';
+import { LoadIntegrations } from '../Integrations';
import { NavigationLoader } from '../primitives/NavigationLoader';
import { SpaceLayoutServerContext } from '../SpaceLayout';
import { Trademark } from '../TableOfContents/Trademark';
@@ -93,6 +94,8 @@ export async function EmbeddableRootLayout({
/>
+ {/* Embeds load no integrations; this marks them loaded so search stops waiting. */}
+
);
diff --git a/packages/gitbook/src/components/Integrations/LoadIntegrations.tsx b/packages/gitbook/src/components/Integrations/LoadIntegrations.tsx
index b3627f610..3ea19f9cd 100644
--- a/packages/gitbook/src/components/Integrations/LoadIntegrations.tsx
+++ b/packages/gitbook/src/components/Integrations/LoadIntegrations.tsx
@@ -147,6 +147,10 @@ export function useCustomCookieBanner(): CustomCookieBannerStore {
export function LoadIntegrations() {
React.useEffect(() => {
return whenIntegrationsReady(() => {
+ // Integrations must only receive `load` once, even if this is mounted twice.
+ if (integrationsStore.getState().loaded) {
+ return;
+ }
dispatchGitBookIntegrationEvent('load');
integrationsStore.setState({ loaded: true });
});
diff --git a/packages/gitbook/src/components/Search/useSearchController.tsx b/packages/gitbook/src/components/Search/useSearchController.tsx
index d520ec6c5..02896b4c9 100644
--- a/packages/gitbook/src/components/Search/useSearchController.tsx
+++ b/packages/gitbook/src/components/Search/useSearchController.tsx
@@ -5,6 +5,7 @@ import React from 'react';
import { useAI } from '../AI';
import { useTrackEvent } from '../Insights';
+import { useIntegrationsLoaded } from '../Integrations';
import { useBodyLoaded } from '../primitives';
import {
clearLastSearchQuery,
@@ -140,6 +141,7 @@ export function useSearchController(
const trackEvent = useTrackEvent();
const resultsRef = React.useRef(null);
const isLoaded = useBodyLoaded();
+ const integrationsLoaded = useIntegrationsLoaded();
const restoredLastQueryForSiteSpaceRef = React.useRef(null);
React.useEffect(() => {
@@ -169,6 +171,8 @@ export function useSearchController(
const withAI = assistants.length > 0;
const withSearchAI = assistants.filter((assistant) => assistant.mode === 'search').length > 0;
+ // Integration assistants register on window load, so wait for it before treating `ask` as a search query.
+ const askIsSearchQuery = withSearchAI || (!withAI && integrationsLoaded);
// Handle initial ask state on page load, once assistants are ready.
// `ask=` should still bootstrap the assistant on the docs site, so we must
@@ -203,7 +207,7 @@ export function useSearchController(
const query =
prev?.query ??
getLastSearchQuery(siteSpace.id) ??
- (withSearchAI || !withAI ? prev?.ask : null) ??
+ (askIsSearchQuery ? prev?.ask : null) ??
'';
return {
@@ -217,7 +221,7 @@ export function useSearchController(
trackEvent({
type: 'search_open',
});
- }, [state?.open, setSearchState, siteSpace.id, trackEvent, withAI, withSearchAI]);
+ }, [state?.open, setSearchState, siteSpace.id, trackEvent, withAI, askIsSearchQuery]);
const setQuery = React.useCallback(
(value: string) => {
@@ -261,7 +265,7 @@ export function useSearchController(
});
const searchValue =
- state?.query ?? (withSearchAI || !withAI ? state?.ask : null) ?? lastSearchQuery ?? '';
+ state?.query ?? (askIsSearchQuery ? state?.ask : null) ?? lastSearchQuery ?? '';
const searchResultsId = `search-results-${React.useId()}`;
// Only clears the remembered last query and stops any in-flight fetch — it must