diff --git a/.changeset/quiet-groups-link.md b/.changeset/quiet-groups-link.md
new file mode 100644
index 000000000..4f1fb7398
--- /dev/null
+++ b/.changeset/quiet-groups-link.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Stop page group breadcrumbs from linking to a URL that redirects to the group's first page.
diff --git a/packages/gitbook/src/components/PageBody/PageHeader.tsx b/packages/gitbook/src/components/PageBody/PageHeader.tsx
index f406d619a..d63ff3535 100644
--- a/packages/gitbook/src/components/PageBody/PageHeader.tsx
+++ b/packages/gitbook/src/components/PageBody/PageHeader.tsx
@@ -222,9 +222,14 @@ export async function PageHeader(props: {
))}
{ancestors.map((breadcrumb, index) => {
const parentPages = ancestors[index - 1]?.pages ?? revision.pages;
+ // A group's own URL redirects to its first document, so link straight to it.
+ const target =
+ breadcrumb.type === 'group'
+ ? (resolveFirstDocument([breadcrumb], [])?.page ?? breadcrumb)
+ : breadcrumb;
const href = linker.toPathForPage({
pages: revision.pages,
- page: breadcrumb,
+ page: target,
});
return (
@@ -483,15 +488,16 @@ function getPageSiblings(
if (page.hidden && page.id !== activeId) {
continue;
}
- // A group whose subtree has no document resolves to no page — the sidebar renders it as a
- // non-clickable label, so don't offer it as a navigable sibling (its path would 404).
- if (page.type === 'group' && page.id !== activeId && !resolveFirstDocument([page], [])) {
+ // A group's own URL redirects to its first document, so link straight to it. A group whose
+ // subtree has no document resolves to no page, so don't offer it (its path would 404).
+ const target = page.type === 'group' ? resolveFirstDocument([page], [])?.page : page;
+ if (!target) {
continue;
}
result.push({
id: page.id,
title: page.linkTitle || page.title,
- href: context.linker.toPathForPage({ pages: context.revision.pages, page }),
+ href: context.linker.toPathForPage({ pages: context.revision.pages, page: target }),
emoji: page.emoji,
icon: page.icon,
isActive: page.id === activeId,
diff --git a/packages/gitbook/src/lib/references.test.ts b/packages/gitbook/src/lib/references.test.ts
index db8c0e6d8..d8bce5bc4 100644
--- a/packages/gitbook/src/lib/references.test.ts
+++ b/packages/gitbook/src/lib/references.test.ts
@@ -627,12 +627,11 @@ describe('resolveContentRef for direct space links', () => {
{ label: 'API guides' },
{ label: 'Authentification' },
{ label: 'Référence', href: targetSiteSpace.urls.published },
- { label: 'Getting started', icon: null, href: expect.any(String) },
+ { label: 'Getting started', icon: null, href: result?.href },
]);
expect(result?.ancestors?.[0]?.href).toBeUndefined();
expect(result?.ancestors?.[1]?.href).toBeUndefined();
expect(result?.ancestors?.[2]?.href).toBeUndefined();
- expect(result?.ancestors?.[4]?.href).toBeTruthy();
});
it('uses the first document as the target for a page-group reference', async () => {
diff --git a/packages/gitbook/src/lib/references.tsx b/packages/gitbook/src/lib/references.tsx
index e174a2bc3..7ee2db0a0 100644
--- a/packages/gitbook/src/lib/references.tsx
+++ b/packages/gitbook/src/lib/references.tsx
@@ -23,7 +23,7 @@ import {
findGitPageURLTarget,
findPageForGitPageURLTarget,
} from './gitPageURL';
-import { resolvePageId } from './pages';
+import { resolveFirstDocument, resolvePageId } from './pages';
import {
findSiteSpaceBy,
getFallbackSiteSpacePath,
@@ -276,7 +276,14 @@ export async function resolveContentRef(
style={iconStyle}
/>
) : null,
- href: linker.toPathForPage({ page: ancestor, pages: revision.pages }),
+ // A group's own URL redirects to its first document, so link straight to it.
+ href: linker.toPathForPage({
+ page:
+ ancestor.type === 'group'
+ ? (resolveFirstDocument([ancestor], [])?.page ?? ancestor)
+ : ancestor,
+ pages: revision.pages,
+ }),
})) ?? [];
if (!page) {
return null;