mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-01 11:18:24 +02:00
fix(seo): docs breadcrumb structured data should use JSON-LD and filter unliked categories (#10888)
Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
parent
cd7875bf84
commit
45065e8d2b
8 changed files with 107 additions and 59 deletions
|
@ -44,6 +44,7 @@
|
|||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reading-time": "^1.5.0",
|
||||
"schema-dts": "^1.1.2",
|
||||
"srcset": "^4.0.0",
|
||||
"tslib": "^2.6.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
"fs-extra": "^11.1.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"schema-dts": "^1.1.2",
|
||||
"tslib": "^2.6.0",
|
||||
"utility-types": "^3.10.0",
|
||||
"webpack": "^5.88.1"
|
||||
|
|
|
@ -60,6 +60,8 @@ export {
|
|||
getDocsVersionSearchTag,
|
||||
} from './docsSearch';
|
||||
|
||||
export {useBreadcrumbsStructuredData} from './structuredDataUtils';
|
||||
|
||||
export type ActivePlugin = {
|
||||
pluginId: string;
|
||||
pluginData: GlobalPluginData;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs';
|
||||
import type {WithContext, BreadcrumbList} from 'schema-dts';
|
||||
|
||||
export function useBreadcrumbsStructuredData({
|
||||
breadcrumbs,
|
||||
}: {
|
||||
breadcrumbs: PropSidebarBreadcrumbsItem[];
|
||||
}): WithContext<BreadcrumbList> {
|
||||
const {siteConfig} = useDocusaurusContext();
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: breadcrumbs
|
||||
// We filter breadcrumb items without links, they are not allowed
|
||||
// See also https://github.com/facebook/docusaurus/issues/9319#issuecomment-2643560845
|
||||
.filter((breadcrumb) => breadcrumb.href)
|
||||
.map((breadcrumb, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
name: breadcrumb.label,
|
||||
item: `${siteConfig.url}${breadcrumb.href}`,
|
||||
})),
|
||||
};
|
||||
}
|
|
@ -1846,3 +1846,14 @@ declare module '@theme/DocBreadcrumbs/Items/Home' {
|
|||
|
||||
export default function HomeBreadcrumbItem(): ReactNode;
|
||||
}
|
||||
|
||||
declare module '@theme/DocBreadcrumbs/StructuredData' {
|
||||
import type {ReactNode} from 'react';
|
||||
import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
export interface Props {
|
||||
readonly breadcrumbs: PropSidebarBreadcrumbsItem[];
|
||||
}
|
||||
|
||||
export default function DocBreadcrumbsStructuredData(props: Props): ReactNode;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import React, {type ReactNode} from 'react';
|
||||
import Head from '@docusaurus/Head';
|
||||
import {useBreadcrumbsStructuredData} from '@docusaurus/plugin-content-docs/client';
|
||||
import type {Props} from '@theme/DocBreadcrumbs/StructuredData';
|
||||
|
||||
export default function DocBreadcrumbsStructuredData(props: Props): ReactNode {
|
||||
const structuredData = useBreadcrumbsStructuredData({
|
||||
breadcrumbs: props.breadcrumbs,
|
||||
});
|
||||
return (
|
||||
<Head>
|
||||
<script type="application/ld+json">
|
||||
{JSON.stringify(structuredData)}
|
||||
</script>
|
||||
</Head>
|
||||
);
|
||||
}
|
|
@ -13,6 +13,7 @@ import {useHomePageRoute} from '@docusaurus/theme-common/internal';
|
|||
import Link from '@docusaurus/Link';
|
||||
import {translate} from '@docusaurus/Translate';
|
||||
import HomeBreadcrumbItem from '@theme/DocBreadcrumbs/Items/Home';
|
||||
import DocBreadcrumbsStructuredData from '@theme/DocBreadcrumbs/StructuredData';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
|
@ -28,22 +29,13 @@ function BreadcrumbsItemLink({
|
|||
}): ReactNode {
|
||||
const className = 'breadcrumbs__link';
|
||||
if (isLast) {
|
||||
return (
|
||||
<span className={className} itemProp="name">
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
return <span className={className}>{children}</span>;
|
||||
}
|
||||
return href ? (
|
||||
<Link className={className} href={href} itemProp="item">
|
||||
<span itemProp="name">{children}</span>
|
||||
<Link className={className} href={href}>
|
||||
<span>{children}</span>
|
||||
</Link>
|
||||
) : (
|
||||
// TODO Google search console doesn't like breadcrumb items without href.
|
||||
// The schema doesn't seem to require `id` for each `item`, although Google
|
||||
// insist to infer one, even if it's invalid. Removing `itemProp="item
|
||||
// name"` for now, since I don't know how to properly fix it.
|
||||
// See https://github.com/facebook/docusaurus/issues/7241
|
||||
<span className={className}>{children}</span>
|
||||
);
|
||||
}
|
||||
|
@ -52,26 +44,16 @@ function BreadcrumbsItemLink({
|
|||
function BreadcrumbsItem({
|
||||
children,
|
||||
active,
|
||||
index,
|
||||
addMicrodata,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
active?: boolean;
|
||||
index: number;
|
||||
addMicrodata: boolean;
|
||||
}): ReactNode {
|
||||
return (
|
||||
<li
|
||||
{...(addMicrodata && {
|
||||
itemScope: true,
|
||||
itemProp: 'itemListElement',
|
||||
itemType: 'https://schema.org/ListItem',
|
||||
})}
|
||||
className={clsx('breadcrumbs__item', {
|
||||
'breadcrumbs__item--active': active,
|
||||
})}>
|
||||
{children}
|
||||
<meta itemProp="position" content={String(index + 1)} />
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
@ -85,40 +67,36 @@ export default function DocBreadcrumbs(): ReactNode {
|
|||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={clsx(
|
||||
ThemeClassNames.docs.docBreadcrumbs,
|
||||
styles.breadcrumbsContainer,
|
||||
)}
|
||||
aria-label={translate({
|
||||
id: 'theme.docs.breadcrumbs.navAriaLabel',
|
||||
message: 'Breadcrumbs',
|
||||
description: 'The ARIA label for the breadcrumbs',
|
||||
})}>
|
||||
<ul
|
||||
className="breadcrumbs"
|
||||
itemScope
|
||||
itemType="https://schema.org/BreadcrumbList">
|
||||
{homePageRoute && <HomeBreadcrumbItem />}
|
||||
{breadcrumbs.map((item, idx) => {
|
||||
const isLast = idx === breadcrumbs.length - 1;
|
||||
const href =
|
||||
item.type === 'category' && item.linkUnlisted
|
||||
? undefined
|
||||
: item.href;
|
||||
return (
|
||||
<BreadcrumbsItem
|
||||
key={idx}
|
||||
active={isLast}
|
||||
index={idx}
|
||||
addMicrodata={!!href}>
|
||||
<BreadcrumbsItemLink href={href} isLast={isLast}>
|
||||
{item.label}
|
||||
</BreadcrumbsItemLink>
|
||||
</BreadcrumbsItem>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
<>
|
||||
<DocBreadcrumbsStructuredData breadcrumbs={breadcrumbs} />
|
||||
<nav
|
||||
className={clsx(
|
||||
ThemeClassNames.docs.docBreadcrumbs,
|
||||
styles.breadcrumbsContainer,
|
||||
)}
|
||||
aria-label={translate({
|
||||
id: 'theme.docs.breadcrumbs.navAriaLabel',
|
||||
message: 'Breadcrumbs',
|
||||
description: 'The ARIA label for the breadcrumbs',
|
||||
})}>
|
||||
<ul className="breadcrumbs">
|
||||
{homePageRoute && <HomeBreadcrumbItem />}
|
||||
{breadcrumbs.map((item, idx) => {
|
||||
const isLast = idx === breadcrumbs.length - 1;
|
||||
const href =
|
||||
item.type === 'category' && item.linkUnlisted
|
||||
? undefined
|
||||
: item.href;
|
||||
return (
|
||||
<BreadcrumbsItem key={idx} active={isLast}>
|
||||
<BreadcrumbsItemLink href={href} isLast={isLast}>
|
||||
{item.label}
|
||||
</BreadcrumbsItemLink>
|
||||
</BreadcrumbsItem>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -47,8 +47,7 @@
|
|||
"@docusaurus/core": "3.7.0",
|
||||
"@docusaurus/types": "3.7.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"schema-dts": "^1.1.2"
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/plugin-content-docs": "*",
|
||||
|
|
Loading…
Add table
Reference in a new issue