From a833fe599304b29e0ee0ea3501a1747ef4890ac6 Mon Sep 17 00:00:00 2001
From: Honey Tyagi <78690656+HoneyTyagii@users.noreply.github.com>
Date: Fri, 10 Jul 2026 16:00:05 +0530
Subject: [PATCH 1/4] docs: fix grammar in slug front matter descriptions
(#12261)
---
website/docs/api/plugins/plugin-content-blog.mdx | 2 +-
website/docs/api/plugins/plugin-content-docs.mdx | 2 +-
website/docs/api/plugins/plugin-content-pages.mdx | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx
index 23cccd58bd39..9f4c3d8f5752 100644
--- a/website/docs/api/plugins/plugin-content-blog.mdx
+++ b/website/docs/api/plugins/plugin-content-blog.mdx
@@ -263,7 +263,7 @@ Accepted fields:
| `keywords` | `string[]` | `undefined` | Keywords meta tag, which will become the `` in `
`, used by search engines. |
| `description` | `string` | The first line of Markdown content | The description of your document, which will become the `` and `` in ``, used by search engines. |
| `image` | `string` | `undefined` | Cover or thumbnail image that will be used as the `` in the ``, enhancing link previews on social media and messaging platforms. |
-| `slug` | `string` | File path | Allows to customize the blog post URL (`//`). Support multiple patterns: `slug: my-blog-post`, `slug: /my/path/to/blog/post`, slug: `/`. |
+| `slug` | `string` | File path | Allows to customize the blog post URL (`//`). Supports multiple patterns: `slug: my-blog-post`, `slug: /my/path/to/blog/post`, slug: `/`. |
| `last_update` | `FrontMatterLastUpdate` | `undefined` | Allows overriding the last update author/date. Date can be any [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). |
```mdx-code-block
diff --git a/website/docs/api/plugins/plugin-content-docs.mdx b/website/docs/api/plugins/plugin-content-docs.mdx
index 38ef345599e5..59379c1eed4d 100644
--- a/website/docs/api/plugins/plugin-content-docs.mdx
+++ b/website/docs/api/plugins/plugin-content-docs.mdx
@@ -296,7 +296,7 @@ Accepted fields:
| `keywords` | `string[]` | `undefined` | Keywords meta tag for the document page, for search engines. |
| `description` | `string` | The first line of Markdown content | The description of your document, which will become the `` and `` in ``, used by search engines. |
| `image` | `string` | `undefined` | Cover or thumbnail image that will be used as the `` in the ``, enhancing link previews on social media and messaging platforms. |
-| `slug` | `string` | File path | Allows to customize the document URL (`//`). Support multiple patterns: `slug: my-doc`, `slug: /my/path/myDoc`, `slug: /`. |
+| `slug` | `string` | File path | Allows to customize the document URL (`//`). Supports multiple patterns: `slug: my-doc`, `slug: /my/path/myDoc`, `slug: /`. |
| `tags` | `Tag[]` | `undefined` | A list of strings or objects of two string fields `label` and `permalink` to tag to your docs. Strings can be a reference to keys of a [tags file](#tags-file) (usually `tags.yml`) |
| `draft` | `boolean` | `false` | Draft documents will only be available during development. |
| `unlisted` | `boolean` | `false` | Unlisted documents will be available in both development and production. They will be "hidden" in production, not indexed, excluded from sitemaps, and can only be accessed by users having a direct link. |
diff --git a/website/docs/api/plugins/plugin-content-pages.mdx b/website/docs/api/plugins/plugin-content-pages.mdx
index 5a6d41bd0132..aff3d0c2531a 100644
--- a/website/docs/api/plugins/plugin-content-pages.mdx
+++ b/website/docs/api/plugins/plugin-content-pages.mdx
@@ -113,7 +113,7 @@ Accepted fields:
| `description` | `string` | The first line of Markdown content | The description of your page, which will become the `` and `` in ``, used by search engines. |
| `keywords` | `string[]` | `undefined` | Keywords meta tag, which will become the `` in ``, used by search engines. |
| `image` | `string` | `undefined` | Cover or thumbnail image that will be used as the `` in the ``, enhancing link previews on social media and messaging platforms. |
-| `slug` | `string` | File path | Allows to customize the page URL (`//`). Support multiple patterns: `slug: my-page`, `slug: /my/page`, slug: `/`. |
+| `slug` | `string` | File path | Allows to customize the page URL (`//`). Supports multiple patterns: `slug: my-page`, `slug: /my/page`, slug: `/`. |
| `wrapperClassName` | `string` | | Class name to be added to the wrapper element to allow targeting specific page content. |
| `hide_table_of_contents` | `boolean` | `false` | Whether to hide the table of contents to the right. |
| `draft` | `boolean` | `false` | Draft pages will only be available during development. |
From 90ea7110c28409d29204debec3f72665c074d317 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Francisco=20Mar=C3=ADn?=
<50973629+franmc01@users.noreply.github.com>
Date: Fri, 10 Jul 2026 05:30:45 -0500
Subject: [PATCH 2/4] docs: fix versionPersistence default value in theme
config table (#12258)
---
website/docs/api/themes/theme-configuration.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx
index 62adb1f63ecf..2b1e0cab2a5b 100644
--- a/website/docs/api/themes/theme-configuration.mdx
+++ b/website/docs/api/themes/theme-configuration.mdx
@@ -170,7 +170,7 @@ Our [main themes](./overview.mdx) offer additional theme configuration options f
| Name | Type | Default | Description |
| --- | --- | --- | --- |
-| `versionPersistence` | `'localStorage' \| 'none'` | `undefined` | Defines the browser persistence of the preferred docs version. |
+| `versionPersistence` | `'localStorage' \| 'none'` | `'localStorage'` | Defines the browser persistence of the preferred docs version. |
| `sidebar.hideable` | `boolean` | `false` | Show a hide button at the bottom of the sidebar. |
| `sidebar.autoCollapseCategories` | `boolean` | `false` | Automatically collapse all sibling categories of the one you navigate to. |
From 625ec4aec69935b369eec8a00a33a54073194f95 Mon Sep 17 00:00:00 2001
From: spokodev
Date: Fri, 10 Jul 2026 11:52:08 +0100
Subject: [PATCH 3/4] fix(utils-common): keep dollar signs when applying
trailing slash (#12219)
Co-authored-by: sebastien
---
.../src/__tests__/applyTrailingSlash.test.ts | 9 +++++++++
.../docusaurus-utils-common/src/applyTrailingSlash.ts | 3 ++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts b/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts
index 629a4c49b2ee..64198f9190c6 100644
--- a/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts
+++ b/packages/docusaurus-utils-common/src/__tests__/applyTrailingSlash.test.ts
@@ -90,6 +90,15 @@ describe('applyTrailingSlash', () => {
expect(applyTrailingSlash('/abc/', params(undefined))).toBe('/abc/');
});
+ it('keeps dollar signs in the pathname', () => {
+ // Regression test for edge case bug
+ // see https://github.com/facebook/docusaurus/pull/12219
+ expect(applyTrailingSlash('/docs/a$$b', params(true))).toBe('/docs/a$$b/');
+ expect(applyTrailingSlash('/docs/a$&b', params(true))).toBe('/docs/a$&b/');
+ expect(applyTrailingSlash("/docs/a$'b", params(true))).toBe("/docs/a$'b/");
+ expect(applyTrailingSlash('/docs/a$$b/', params(false))).toBe('/docs/a$$b');
+ });
+
it('applies to path with #anchor', () => {
expect(applyTrailingSlash('/abc#anchor', params(true))).toBe(
'/abc/#anchor',
diff --git a/packages/docusaurus-utils-common/src/applyTrailingSlash.ts b/packages/docusaurus-utils-common/src/applyTrailingSlash.ts
index f83e5ce9c076..58e5141fa4e8 100644
--- a/packages/docusaurus-utils-common/src/applyTrailingSlash.ts
+++ b/packages/docusaurus-utils-common/src/applyTrailingSlash.ts
@@ -40,6 +40,7 @@ export default function applyTrailingSlash(
// The trailing slash should be handled before the ?search#hash !
const [pathname] = path.split(/[#?]/) as [string, ...string[]];
+ const queryHash = path.slice(pathname.length);
// Never transform '/' to ''
// Never remove the baseUrl trailing slash!
@@ -51,7 +52,7 @@ export default function applyTrailingSlash(
? pathname
: handleTrailingSlash(pathname, trailingSlash);
- return path.replace(pathname, newPathname);
+ return `${newPathname}${queryHash}`;
}
/** Appends a leading slash to `str`, if one doesn't exist. */
From 54556428dc3d038aafca1ca58694c04081829e92 Mon Sep 17 00:00:00 2001
From: Aaron Thomas <158775699+ATKasem@users.noreply.github.com>
Date: Fri, 10 Jul 2026 07:24:56 -0500
Subject: [PATCH 4/4] fix(blog, docs): apply trailingSlash to blog structured
data URLs (#12262)
Co-authored-by: sebastien
---
.../src/client/structuredDataUtils.ts | 20 ++++++++++---
.../src/client/structuredDataUtils.ts | 28 +++++++++++++++++--
.../src/applyTrailingSlash.ts | 5 +++-
3 files changed, 45 insertions(+), 8 deletions(-)
diff --git a/packages/docusaurus-plugin-content-blog/src/client/structuredDataUtils.ts b/packages/docusaurus-plugin-content-blog/src/client/structuredDataUtils.ts
index a541fc7705c7..550e25d2c543 100644
--- a/packages/docusaurus-plugin-content-blog/src/client/structuredDataUtils.ts
+++ b/packages/docusaurus-plugin-content-blog/src/client/structuredDataUtils.ts
@@ -8,6 +8,7 @@
import {useBaseUrlUtils, type BaseUrlUtils} from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {useBlogMetadata} from '@docusaurus/plugin-content-blog/client';
+import {applyTrailingSlash} from '@docusaurus/utils-common';
import type {Props as BlogListPageStructuredDataProps} from '@theme/BlogListPage/StructuredData';
import {useBlogPost} from './contexts';
@@ -26,6 +27,17 @@ import type {DocusaurusConfig} from '@docusaurus/types';
const convertDate = (dateMs: number) => new Date(dateMs).toISOString();
+function getAbsoluteUrl(
+ permalink: string,
+ siteConfig: DocusaurusConfig,
+): string {
+ const absoluteUrl = `${siteConfig.url}${permalink}`;
+ return applyTrailingSlash(absoluteUrl, {
+ trailingSlash: siteConfig.trailingSlash,
+ baseUrl: siteConfig.baseUrl,
+ });
+}
+
function getBlogPost(
blogPostContent: PropBlogPostContent,
siteConfig: DocusaurusConfig,
@@ -37,7 +49,7 @@ function getBlogPost(
const image = assets.image ?? frontMatter.image;
const keywords = frontMatter.keywords ?? [];
- const blogUrl = `${siteConfig.url}${metadata.permalink}`;
+ const blogUrl = getAbsoluteUrl(metadata.permalink, siteConfig);
const dateModified = lastUpdatedAt ? convertDate(lastUpdatedAt) : undefined;
@@ -92,7 +104,7 @@ export function useBlogListPageStructuredData(
metadata: {blogDescription, blogTitle, permalink},
} = props;
- const url = `${siteConfig.url}${permalink}`;
+ const url = getAbsoluteUrl(permalink, siteConfig);
// details on structured data support: https://schema.org/Blog
return {
@@ -121,7 +133,7 @@ export function useBlogPostStructuredData(): WithContext {
const dateModified = lastUpdatedAt ? convertDate(lastUpdatedAt) : undefined;
- const url = `${siteConfig.url}${metadata.permalink}`;
+ const url = getAbsoluteUrl(metadata.permalink, siteConfig);
// details on structured data support: https://schema.org/BlogPosting
// BlogPosting is one of the structured data types that Google explicitly
@@ -142,7 +154,7 @@ export function useBlogPostStructuredData(): WithContext {
...(keywords ? {keywords} : {}),
isPartOf: {
'@type': 'Blog',
- '@id': `${siteConfig.url}${blogMetadata.blogBasePath}`,
+ '@id': getAbsoluteUrl(blogMetadata.blogBasePath, siteConfig),
name: blogMetadata.blogTitle,
},
};
diff --git a/packages/docusaurus-plugin-content-docs/src/client/structuredDataUtils.ts b/packages/docusaurus-plugin-content-docs/src/client/structuredDataUtils.ts
index 01367a43881e..806353346313 100644
--- a/packages/docusaurus-plugin-content-docs/src/client/structuredDataUtils.ts
+++ b/packages/docusaurus-plugin-content-docs/src/client/structuredDataUtils.ts
@@ -6,15 +6,28 @@
*/
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import {applyTrailingSlash} from '@docusaurus/utils-common';
import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs';
import type {WithContext, BreadcrumbList} from 'schema-dts';
+import type {DocusaurusConfig} from '@docusaurus/types';
-export function useBreadcrumbsStructuredData({
+type Params = Pick;
+
+function getAbsoluteUrl(permalink: string, params: Params): string {
+ const absoluteUrl = `${params.url}${permalink}`;
+ return applyTrailingSlash(absoluteUrl, {
+ trailingSlash: params.trailingSlash,
+ baseUrl: params.baseUrl,
+ });
+}
+
+function getBreadcrumbsStructuredData({
breadcrumbs,
+ params,
}: {
breadcrumbs: PropSidebarBreadcrumbsItem[];
+ params: Params;
}): WithContext {
- const {siteConfig} = useDocusaurusContext();
return {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
@@ -26,7 +39,16 @@ export function useBreadcrumbsStructuredData({
'@type': 'ListItem',
position: index + 1,
name: breadcrumb.label,
- item: `${siteConfig.url}${breadcrumb.href}`,
+ item: getAbsoluteUrl(breadcrumb.href!, params),
})),
};
}
+
+export function useBreadcrumbsStructuredData({
+ breadcrumbs,
+}: {
+ breadcrumbs: PropSidebarBreadcrumbsItem[];
+}): WithContext {
+ const {siteConfig} = useDocusaurusContext();
+ return getBreadcrumbsStructuredData({breadcrumbs, params: siteConfig});
+}
diff --git a/packages/docusaurus-utils-common/src/applyTrailingSlash.ts b/packages/docusaurus-utils-common/src/applyTrailingSlash.ts
index 58e5141fa4e8..6e8b63fd4365 100644
--- a/packages/docusaurus-utils-common/src/applyTrailingSlash.ts
+++ b/packages/docusaurus-utils-common/src/applyTrailingSlash.ts
@@ -17,7 +17,10 @@ export function addTrailingSlash(str: string): string {
return str.endsWith('/') ? str : `${str}/`;
}
-// Trailing slash handling depends in some site configuration options
+/**
+ * Apply/remove a trailing slash on an URL path according to site config options
+ * Usually applied on permalinks / URL paths, but also works with absolute URL
+ */
export default function applyTrailingSlash(
path: string,
options: ApplyTrailingSlashParams,