Fix search results linking to section anchors on title matches (RND-11916)

Use the API `resultType` on page search results to decide the link target:
page/title-level matches link to the top of the page, while section-level
matches keep their section anchor. The section snippet is still shown as a
preview. `resultType` is read defensively since it is not yet present in the
published `@gitbook/api` types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Zeno
2026-07-31 00:18:28 +00:00
parent f9ad9b5356
commit 8340e16930
4 changed files with 28 additions and 3 deletions
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Search results from a page/title-level match now link to the top of the page instead of a section anchor; only section-level matches deep-link to their section.
@@ -143,6 +143,12 @@ function transformSitePageResult(args: {
? toEmbeddableLinkForPublishedContent(linker, spaceURL, pageItem.path)
: linker.toLinkForContent(joinPathWithBaseURL(spaceURL, pageItem.path));
// `resultType` marks whether the API matched the page at the title/page level or on a
// specific section. It is read defensively because it is not yet in the published
// `@gitbook/api` types; once present, page-level matches link to the top of the page.
const resultType = (pageItem as SearchPageResult & { resultType?: 'page' | 'section' })
.resultType;
const page: ComputedPageResult = {
type: 'page',
id: `${spaceItem.id}/${pageItem.id}`,
@@ -151,6 +157,7 @@ function transformSitePageResult(args: {
pageId: pageItem.id,
spaceId: spaceItem.id,
score: pageItem.score,
resultType,
breadcrumbs,
};
@@ -26,9 +26,15 @@ export const SearchPageResultItem = React.forwardRef(function SearchPageResultIt
const language = useLanguage();
const bestSection = item.type === 'page' ? item.bestSection : undefined;
// When the section snippet is displayed, link to the section anchor so the
// link matches what the user sees.
const href = bestSection?.body ? bestSection.href : 'href' in item ? item.href : item.pathname;
// Section-level matches deep-link to their section anchor; page/title-level matches
// link to the top of the page even when a section snippet is shown as a preview.
const isPageLevelMatch = item.type === 'page' && item.resultType === 'page';
const href =
bestSection?.body && !isPageLevelMatch
? bestSection.href
: 'href' in item
? item.href
: item.pathname;
const emoji = 'emoji' in item ? item.emoji : undefined;
const icon = 'icon' in item ? item.icon : undefined;
@@ -20,6 +20,13 @@ export type ComputedPageResult = BaseComputedResult & {
type: 'page';
pageId: string;
spaceId: string;
/**
* Whether the API matched this result at the page/title level or on a specific
* section. Page-level matches link to the top of the page; section-level matches
* keep the section anchor. Optional to stay backward-compatible with API responses
* that do not yet return it.
*/
resultType?: 'page' | 'section';
breadcrumbs?: Array<{ icon?: IconName; label: string }>;
/** The highest-scoring section for this page, used as a body snippet preview. */
bestSection?: {