diff --git a/packages/gitbook/next.config.mjs b/packages/gitbook/next.config.mjs index d915cf312..4dbe0d3ff 100644 --- a/packages/gitbook/next.config.mjs +++ b/packages/gitbook/next.config.mjs @@ -14,6 +14,9 @@ const nextConfig = { dynamic: 3600, // 1 hour static: 3600, // 1 hour }, + + // Since content is fully static, we don't want to fetch on hover again + optimisticClientCache: false, }, env: { diff --git a/packages/gitbook/src/components/SiteLayout/ClientContexts.tsx b/packages/gitbook/src/components/SiteLayout/ClientContexts.tsx index ddeabdf62..c87550228 100644 --- a/packages/gitbook/src/components/SiteLayout/ClientContexts.tsx +++ b/packages/gitbook/src/components/SiteLayout/ClientContexts.tsx @@ -3,15 +3,19 @@ import type { CustomizationThemeMode, SiteExternalLinksTarget } from '@gitbook/api'; import { ThemeProvider } from 'next-themes'; import type React from 'react'; +import { useClearRouterCache } from '../hooks/useClearRouterCache'; import { LinkSettingsContext } from '../primitives'; export function ClientContexts(props: { nonce?: string; forcedTheme: CustomizationThemeMode | undefined; externalLinksTarget: SiteExternalLinksTarget; + contextId: string | undefined; children: React.ReactNode; }) { - const { children, forcedTheme, externalLinksTarget } = props; + const { children, forcedTheme, externalLinksTarget, contextId } = props; + + useClearRouterCache(contextId); /** * A bug in ThemeProvider is causing the nonce to be included incorrectly diff --git a/packages/gitbook/src/components/SiteLayout/SiteLayout.tsx b/packages/gitbook/src/components/SiteLayout/SiteLayout.tsx index e667aa187..eb5763dce 100644 --- a/packages/gitbook/src/components/SiteLayout/SiteLayout.tsx +++ b/packages/gitbook/src/components/SiteLayout/SiteLayout.tsx @@ -9,12 +9,11 @@ import { AdminToolbar } from '@/components/AdminToolbar'; import { CookiesToast } from '@/components/Cookies'; import { LoadIntegrations } from '@/components/Integrations'; import { SpaceLayout } from '@/components/SpaceLayout'; -import { buildVersion } from '@/lib/build'; -import { isSiteIndexable } from '@/lib/seo'; - import type { VisitorAuthClaims } from '@/lib/adaptive'; +import { buildVersion } from '@/lib/build'; import { GITBOOK_API_PUBLIC_URL, GITBOOK_ASSETS_URL, GITBOOK_ICONS_URL } from '@/lib/env'; import { getResizedImageURL } from '@/lib/images'; +import { isSiteIndexable } from '@/lib/seo'; import { ClientContexts } from './ClientContexts'; import { RocketLoaderDetector } from './RocketLoaderDetector'; @@ -50,6 +49,7 @@ export async function SiteLayout(props: { { + if (previousContextId === undefined) { + // On the first run, we set the previousContextId to the current contextId + previousContextId = contextId; + return; // Skip the first run to avoid unnecessary reload + } + // Initially, previousContextId will be undefined, so we only clear the cache + // if contextId has changed from a defined value to a new value. + // This prevents unnecessary cache clearing on the first render. + if (contextId !== previousContextId && previousContextId !== undefined) { + previousContextId = contextId; + // Trigger a full reload to clear the in-memory cache + router.refresh(); // This will clear the cache and re-fetch the data + } + }, [contextId, router]); +} diff --git a/packages/gitbook/src/components/primitives/Link.tsx b/packages/gitbook/src/components/primitives/Link.tsx index 0550867ea..4f1242607 100644 --- a/packages/gitbook/src/components/primitives/Link.tsx +++ b/packages/gitbook/src/components/primitives/Link.tsx @@ -123,11 +123,15 @@ export const Link = React.forwardRef(function Link( ); } + // Not sure why yet, but it seems necessary to force prefetch to true + // default behavior doesn't seem to properly use the client router cache. + const _prefetch = prefetch === null || prefetch === undefined ? true : prefetch; + return ( & { /** * Identifier used for image resizing. @@ -123,6 +124,9 @@ export type GitBookSiteContext = GitBookSpaceContext & { /** Scripts to load for the site. */ scripts: SiteIntegrationScript[]; + + /** Context ID used by adaptive content. It represents an unique identifier for the authentication context */ + contextId?: string; }; /** @@ -200,6 +204,7 @@ export async function fetchSiteContextByURLLookup( shareKey: data.shareKey, changeRequest: data.changeRequest, revision: data.revision, + contextId: data.contextId, }); } @@ -217,6 +222,7 @@ export async function fetchSiteContextByIds( shareKey: string | undefined; changeRequest: string | undefined; revision: string | undefined; + contextId?: string; } ): Promise { const { dataFetcher } = baseContext; @@ -314,6 +320,7 @@ export async function fetchSiteContextByIds( structure: siteStructure, sections, scripts, + contextId: ids.contextId, }; } diff --git a/packages/gitbook/src/middleware.ts b/packages/gitbook/src/middleware.ts index 45f9cef30..161d3039e 100644 --- a/packages/gitbook/src/middleware.ts +++ b/packages/gitbook/src/middleware.ts @@ -249,6 +249,7 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) { shareKey: siteURLData.shareKey, apiToken: siteURLData.apiToken, imagesContextId: imagesContextId, + contextId: siteURLData.contextId, }; const requestHeaders = new Headers(request.headers); @@ -328,6 +329,12 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) { response.headers.set('x-gitbook-route-type', routeType); response.headers.set('x-gitbook-route-site', siteURLWithoutProtocol); + // When we use adaptive content, we want to ensure that the cache is not used at all on the client side. + // Vercel already set this header, this is needed in OpenNext. + if (siteURLData.contextId) { + response.headers.set('cache-control', 'public, max-age=0, must-revalidate'); + } + return writeResponseCookies(response, cookies); };