facebook/docusaurus

useSearchResultUrlProcessor drops query strings from internal URLs

Summary

  • Context: The useSearchResultUrlProcessor hook processes Algolia search result URLs to convert them into relative URLs for internal SPA navigation, or keep them as absolute URLs for external sites.

  • Bug: The function drops query strings (search parameters) from internal URLs when converting them to relative URLs.

  • Actual vs. expected: Internal URLs with query strings like /docs?version=2.0#section are converted to /docs#section (missing ?version=2.0), when they should preserve all URL components.

  • Impact: Any Algolia search results containing query strings will lose those parameters when users navigate to them, potentially breaking functionality that depends on query parameters (e.g., version selection, language switching, tab navigation).

Code with bug

// packages/docusaurus-theme-search-algolia/src/client/useSearchResultUrlProcessor.ts
export function useSearchResultUrlProcessor(): (url: string) => string {
  const { withBaseUrl } = useBaseUrlUtils();

  const {
    algolia: { externalUrlRegex, replaceSearchResultPathname },
  } = useAlgoliaThemeConfig();

  return useCallback(
    (url: string) => {
      const parsedURL = new URL(url);

      // Algolia contains an external domain => navigate to URL
      if (isRegexpStringMatch(externalUrlRegex, parsedURL.href)) {
        return url;
      }

      // Otherwise => transform to relative URL for SPA navigation
      const relativeUrl = `${parsedURL.pathname + parsedURL.hash}`;
      // <-- BUG 🔴 Missing parsedURL.search

      return withBaseUrl(
        replacePathname(
          relativeUrl,
          replaceSearchResultPathname,
        ),
      );
    },
    [
      withBaseUrl,
      externalUrlRegex,
      replaceSearchResultPathname,
    ],
  );
}

Example

Given URL: https://example.com/docs/api?tab=examples&version=2.0#methods

  • Parse components:

    • parsedURL.pathname = /docs/api

    • parsedURL.search = ?tab=examples&version=2.0

    • parsedURL.hash = #methods

  • Buggy construction: parsedURL.pathname + parsedURL.hash → /docs/api#methods

  • Actual result: navigates to /docs/api#methods (query string lost)

  • Expected result: /docs/api?tab=examples&version=2.0#methods

Recommended fix

Replace the relative URL construction to include all URL components:

const relativeUrl =parsedURL.pathname{parsedURL.search}${parsedURL.hash}; // <-- FIX 🟢