Skip to content

Commit d67933c

Browse files
committed
feat: migrate breadcrumbs structured data to json-ld
1 parent 7ca3aa5 commit d67933c

File tree

3 files changed

+80
-57
lines changed

3 files changed

+80
-57
lines changed

packages/docusaurus-theme-classic/src/theme-classic.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,3 +1846,14 @@ declare module '@theme/DocBreadcrumbs/Items/Home' {
18461846

18471847
export default function HomeBreadcrumbItem(): ReactNode;
18481848
}
1849+
1850+
declare module '@theme/DocBreadcrumbs/StructuredData' {
1851+
import type {ReactNode} from 'react';
1852+
import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs';
1853+
1854+
export interface Props {
1855+
readonly breadcrumbs: PropSidebarBreadcrumbsItem[];
1856+
}
1857+
1858+
export default function DocBreadcrumbsStructuredData(props: Props): ReactNode;
1859+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React, {type ReactNode} from 'react';
9+
import Head from '@docusaurus/Head';
10+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
11+
import type {Props} from '@theme/DocBreadcrumbs/StructuredData';
12+
13+
export default function DocBreadcrumbsStructuredData(props: Props): ReactNode {
14+
const {siteConfig} = useDocusaurusContext();
15+
const structuredData = {
16+
'@context': 'https://schema.org',
17+
'@type': 'BreadcrumbList',
18+
itemListElement: props.breadcrumbs
19+
.filter((breadcrumb) => breadcrumb.href)
20+
.map((breadcrumb, index) => ({
21+
'@type': 'ListItem',
22+
position: index + 1,
23+
name: breadcrumb.label,
24+
item: `${siteConfig.url}${breadcrumb.href}`,
25+
})),
26+
};
27+
return (
28+
<Head>
29+
<script type="application/ld+json">
30+
{JSON.stringify(structuredData)}
31+
</script>
32+
</Head>
33+
);
34+
}

packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {useHomePageRoute} from '@docusaurus/theme-common/internal';
1313
import Link from '@docusaurus/Link';
1414
import {translate} from '@docusaurus/Translate';
1515
import HomeBreadcrumbItem from '@theme/DocBreadcrumbs/Items/Home';
16+
import DocBreadcrumbsStructuredData from '@theme/DocBreadcrumbs/StructuredData';
1617

1718
import styles from './styles.module.css';
1819

@@ -28,22 +29,13 @@ function BreadcrumbsItemLink({
2829
}): ReactNode {
2930
const className = 'breadcrumbs__link';
3031
if (isLast) {
31-
return (
32-
<span className={className} itemProp="name">
33-
{children}
34-
</span>
35-
);
32+
return <span className={className}>{children}</span>;
3633
}
3734
return href ? (
38-
<Link className={className} href={href} itemProp="item">
39-
<span itemProp="name">{children}</span>
35+
<Link className={className} href={href}>
36+
<span>{children}</span>
4037
</Link>
4138
) : (
42-
// TODO Google search console doesn't like breadcrumb items without href.
43-
// The schema doesn't seem to require `id` for each `item`, although Google
44-
// insist to infer one, even if it's invalid. Removing `itemProp="item
45-
// name"` for now, since I don't know how to properly fix it.
46-
// See https://github.com/facebook/docusaurus/issues/7241
4739
<span className={className}>{children}</span>
4840
);
4941
}
@@ -52,26 +44,16 @@ function BreadcrumbsItemLink({
5244
function BreadcrumbsItem({
5345
children,
5446
active,
55-
index,
56-
addMicrodata,
5747
}: {
5848
children: ReactNode;
5949
active?: boolean;
60-
index: number;
61-
addMicrodata: boolean;
6250
}): ReactNode {
6351
return (
6452
<li
65-
{...(addMicrodata && {
66-
itemScope: true,
67-
itemProp: 'itemListElement',
68-
itemType: 'https://schema.org/ListItem',
69-
})}
7053
className={clsx('breadcrumbs__item', {
7154
'breadcrumbs__item--active': active,
7255
})}>
7356
{children}
74-
<meta itemProp="position" content={String(index + 1)} />
7557
</li>
7658
);
7759
}
@@ -85,40 +67,36 @@ export default function DocBreadcrumbs(): ReactNode {
8567
}
8668

8769
return (
88-
<nav
89-
className={clsx(
90-
ThemeClassNames.docs.docBreadcrumbs,
91-
styles.breadcrumbsContainer,
92-
)}
93-
aria-label={translate({
94-
id: 'theme.docs.breadcrumbs.navAriaLabel',
95-
message: 'Breadcrumbs',
96-
description: 'The ARIA label for the breadcrumbs',
97-
})}>
98-
<ul
99-
className="breadcrumbs"
100-
itemScope
101-
itemType="https://schema.org/BreadcrumbList">
102-
{homePageRoute && <HomeBreadcrumbItem />}
103-
{breadcrumbs.map((item, idx) => {
104-
const isLast = idx === breadcrumbs.length - 1;
105-
const href =
106-
item.type === 'category' && item.linkUnlisted
107-
? undefined
108-
: item.href;
109-
return (
110-
<BreadcrumbsItem
111-
key={idx}
112-
active={isLast}
113-
index={idx}
114-
addMicrodata={!!href}>
115-
<BreadcrumbsItemLink href={href} isLast={isLast}>
116-
{item.label}
117-
</BreadcrumbsItemLink>
118-
</BreadcrumbsItem>
119-
);
120-
})}
121-
</ul>
122-
</nav>
70+
<>
71+
<nav
72+
className={clsx(
73+
ThemeClassNames.docs.docBreadcrumbs,
74+
styles.breadcrumbsContainer,
75+
)}
76+
aria-label={translate({
77+
id: 'theme.docs.breadcrumbs.navAriaLabel',
78+
message: 'Breadcrumbs',
79+
description: 'The ARIA label for the breadcrumbs',
80+
})}>
81+
<ul className="breadcrumbs">
82+
{homePageRoute && <HomeBreadcrumbItem />}
83+
{breadcrumbs.map((item, idx) => {
84+
const isLast = idx === breadcrumbs.length - 1;
85+
const href =
86+
item.type === 'category' && item.linkUnlisted
87+
? undefined
88+
: item.href;
89+
return (
90+
<BreadcrumbsItem key={idx} active={isLast}>
91+
<BreadcrumbsItemLink href={href} isLast={isLast}>
92+
{item.label}
93+
</BreadcrumbsItemLink>
94+
</BreadcrumbsItem>
95+
);
96+
})}
97+
</ul>
98+
</nav>
99+
<DocBreadcrumbsStructuredData breadcrumbs={breadcrumbs} />
100+
</>
123101
);
124102
}

0 commit comments

Comments
 (0)