From 6dd85b810f946544b70398f370aa3cc2e53ce07a Mon Sep 17 00:00:00 2001 From: bh0fer Date: Tue, 25 Nov 2025 23:58:55 +0000 Subject: [PATCH 1/7] feat(core): support custom html elements in head tags https://github.com/facebook/docusaurus/discussions/11570 --- packages/docusaurus-types/src/plugin.d.ts | 2 ++ packages/docusaurus/src/server/configValidation.ts | 11 ++++++++--- packages/docusaurus/src/server/htmlTags.ts | 12 ++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-types/src/plugin.d.ts b/packages/docusaurus-types/src/plugin.d.ts index 7d8a91e8a620..25458e9fd8c1 100644 --- a/packages/docusaurus-types/src/plugin.d.ts +++ b/packages/docusaurus-types/src/plugin.d.ts @@ -104,6 +104,8 @@ export type HtmlTagObject = { tagName: string; /** The inner HTML */ innerHTML?: string; + /** Allow custom html elements, e.g. `` */ + customElement?: boolean; }; export type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[]; diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index f826788adf6e..e57a7ba45abe 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -437,9 +437,14 @@ export const ConfigSchema = Joi.object({ .items( Joi.object({ tagName: Joi.string().required(), - attributes: Joi.object() - .pattern(/[\w-]+/, Joi.string()) - .required(), + attributes: Joi.object().when('customElement', { + is: Joi.valid(true), + then: Joi.optional(), + otherwise: Joi.object() + .pattern(/[\w-]+/, Joi.string()) + .required(), + }), + customElement: Joi.bool().default(false), }).unknown(), ) .messages({ diff --git a/packages/docusaurus/src/server/htmlTags.ts b/packages/docusaurus/src/server/htmlTags.ts index 46bd39ab75cc..c3bf8c23bf39 100644 --- a/packages/docusaurus/src/server/htmlTags.ts +++ b/packages/docusaurus/src/server/htmlTags.ts @@ -21,18 +21,22 @@ function assertIsHtmlTagObject(val: unknown): asserts val is HtmlTagObject { if (typeof val !== 'object' || !val) { throw new Error(`"${val}" is not a valid HTML tag object.`); } - if (typeof (val as HtmlTagObject).tagName !== 'string') { + const htmlTag = val as HtmlTagObject; + if (typeof htmlTag.tagName !== 'string') { throw new Error( `${JSON.stringify( val, )} is not a valid HTML tag object. "tagName" must be defined as a string.`, ); } - if (!(htmlTags as string[]).includes((val as HtmlTagObject).tagName)) { + if ( + !htmlTag.customElement && + !(htmlTags as string[]).includes(htmlTag.tagName) + ) { throw new Error( `Error loading ${JSON.stringify(val)}, "${ - (val as HtmlTagObject).tagName - }" is not a valid HTML tag.`, + htmlTag.tagName + }" is not a valid HTML tag. Either use a valid "tagName" or set "customElement: true".`, ); } } From 2ca4a656750ad07c107e46e45e252ccd584515bf Mon Sep 17 00:00:00 2001 From: bh0fer Date: Tue, 25 Nov 2025 23:59:14 +0000 Subject: [PATCH 2/7] update docs --- website/docs/api/docusaurus.config.js.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/api/docusaurus.config.js.mdx b/website/docs/api/docusaurus.config.js.mdx index a376628fb02b..0d79abf740dc 100644 --- a/website/docs/api/docusaurus.config.js.mdx +++ b/website/docs/api/docusaurus.config.js.mdx @@ -767,9 +767,9 @@ export default { ### `headTags` {#headTags} -An array of tags that will be inserted in the HTML ``. The values must be objects that contain two properties; `tagName` and `attributes`. `tagName` must be a string that determines the tag being created; eg `"link"`. `attributes` must be an attribute-value map. +An array of tags that will be inserted in the HTML ``. The values must be objects that contain two properties; `tagName` and `attributes`. `tagName` must be a string that determines the tag being created; eg `"link"`. `attributes` must be an attribute-value map. When custom html elements are needed, set `customElement: true`. -- Type: `{ tagName: string; attributes: Object; }[]` +- Type: `({ tagName: string; attributes: Object; } | { tagName: string; attributes?: Object; customElement: true; })[]` Example: From 8d606815c24e4808ffbf0bf3bd68019dded98aef Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 11:00:07 +0100 Subject: [PATCH 3/7] add unit test + comment --- .../src/server/__tests__/configValidation.test.ts | 13 +++++++++++++ packages/docusaurus/src/server/htmlTags.ts | 1 + 2 files changed, 14 insertions(+) diff --git a/packages/docusaurus/src/server/__tests__/configValidation.test.ts b/packages/docusaurus/src/server/__tests__/configValidation.test.ts index 98aea0370a30..3b954656e6cb 100644 --- a/packages/docusaurus/src/server/__tests__/configValidation.test.ts +++ b/packages/docusaurus/src/server/__tests__/configValidation.test.ts @@ -315,6 +315,19 @@ describe('headTags', () => { `); }); + it('accepts custom element without attributes', () => { + expect(() => + normalizeConfig({ + headTags: [ + { + tagName: 'my-custom-element', + customElement: true, + }, + ], + }), + ).not.toThrow(); + }); + it("throws error if headTags doesn't have string attributes", () => { expect(() => { normalizeConfig({ diff --git a/packages/docusaurus/src/server/htmlTags.ts b/packages/docusaurus/src/server/htmlTags.ts index c3bf8c23bf39..52cbee825847 100644 --- a/packages/docusaurus/src/server/htmlTags.ts +++ b/packages/docusaurus/src/server/htmlTags.ts @@ -17,6 +17,7 @@ import type { RouterType, } from '@docusaurus/types'; +// TODO this should be done at config validation time, not here function assertIsHtmlTagObject(val: unknown): asserts val is HtmlTagObject { if (typeof val !== 'object' || !val) { throw new Error(`"${val}" is not a valid HTML tag object.`); From fb00132e46bd4e385f02729ab6b0924e44d025aa Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 11:06:49 +0100 Subject: [PATCH 4/7] simplify docs --- website/docs/api/docusaurus.config.js.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/api/docusaurus.config.js.mdx b/website/docs/api/docusaurus.config.js.mdx index 0d79abf740dc..a21ebbcf1782 100644 --- a/website/docs/api/docusaurus.config.js.mdx +++ b/website/docs/api/docusaurus.config.js.mdx @@ -769,7 +769,7 @@ export default { An array of tags that will be inserted in the HTML ``. The values must be objects that contain two properties; `tagName` and `attributes`. `tagName` must be a string that determines the tag being created; eg `"link"`. `attributes` must be an attribute-value map. When custom html elements are needed, set `customElement: true`. -- Type: `({ tagName: string; attributes: Object; } | { tagName: string; attributes?: Object; customElement: true; })[]` +- Type: `{ tagName: string; attributes: Object; customElement?: boolean; }[]` Example: From 099bf59e1227ddd1aa511764c1059b8d51153be3 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 11:08:58 +0100 Subject: [PATCH 5/7] empty From 78d8eab2be552425d019b2e0655a82863bcdc630 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 11:10:08 +0100 Subject: [PATCH 6/7] trigger ci --- .../docusaurus/src/server/__tests__/configValidation.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus/src/server/__tests__/configValidation.test.ts b/packages/docusaurus/src/server/__tests__/configValidation.test.ts index 3b954656e6cb..379ce8af2898 100644 --- a/packages/docusaurus/src/server/__tests__/configValidation.test.ts +++ b/packages/docusaurus/src/server/__tests__/configValidation.test.ts @@ -315,7 +315,7 @@ describe('headTags', () => { `); }); - it('accepts custom element without attributes', () => { + it('accepts headTags with a custom element without attributes', () => { expect(() => normalizeConfig({ headTags: [ From 4c60a5094f9edafd36f60c171403e3338b6a9845 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 11:14:19 +0100 Subject: [PATCH 7/7] update test snapshot --- packages/docusaurus/src/server/__tests__/htmlTags.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus/src/server/__tests__/htmlTags.test.ts b/packages/docusaurus/src/server/__tests__/htmlTags.test.ts index 717e5ca79e03..e46e60d59523 100644 --- a/packages/docusaurus/src/server/__tests__/htmlTags.test.ts +++ b/packages/docusaurus/src/server/__tests__/htmlTags.test.ts @@ -189,7 +189,7 @@ describe('loadHtmlTags', () => { }, ]), ).toThrowErrorMatchingInlineSnapshot( - `"Error loading {"tagName":"endiliey","attributes":{"this":"is invalid"}}, "endiliey" is not a valid HTML tag."`, + `"Error loading {"tagName":"endiliey","attributes":{"this":"is invalid"}}, "endiliey" is not a valid HTML tag. Either use a valid "tagName" or set "customElement: true"."`, ); });