type TagsConfig = HtmlTag | HtmlTagHandler | Array<HtmlTag | HtmlTagHandler>;undefinedModifies the tags that are injected into the HTML page.
In Rsbuild plugins, you can modify tags by using api.modifyHTMLTags hook.
For example, add a script tag to the head and inject it after the existing tags:
export default {
html: {
tags: [
{
tag: 'script',
attrs: { src: 'https://example.com/script.js' },
},
],
},
};The generated HTML file will look like:
<html>
<head>
<!-- some other head tags... -->
<script src="https://example.com/script.js"></script>
</head>
<body>
<!-- some other body tags... -->
</body>
</html>html.tags can accept an array, where each element is a HtmlTag object that describes the tag to be injected.
type HtmlTag = {
tag: string;
attrs?: Record<string, string | boolean | null | undefined>;
children?: string;
hash?: boolean | string | ((url: string, hash: string) => string);
publicPath?: boolean | string | ((url: string, publicPath: string) => string);
append?: boolean;
head?: boolean;
metadata?: Record<string, any>;
};stringundefinedThe HTML tag name to be generated. Should be a valid HTML element name.
For example, inject a script tag and a link tag:
export default {
html: {
tags: [
// Result: <script src="https://example.com/script.js"></script>
{
tag: 'script',
attrs: { src: 'https://example.com/script.js' },
},
// Result: <link href="https://example.com/style.css" rel="stylesheet">
{
tag: 'link',
attrs: { href: 'https://example.com/style.css', rel: 'stylesheet' },
},
],
},
};Record<string, string | boolean | null | undefined>undefinedHTML attributes to be applied to the tag.
string values will be rendered as attribute="value"true renders as boolean attribute (e.g., <input disabled>)false or null or undefined values will omit the attributeFor example, inject a script tag with src and type attributes:
export default {
html: {
tags: [
// Result: <script src="https://example.com/script.js" type="text/javascript" defer></script>
{
tag: 'script',
attrs: {
src: 'https://example.com/script.js',
type: 'text/javascript',
defer: true,
},
},
],
},
};stringundefinedThe innerHTML content of the tag. The content is inserted as-is without HTML escaping, so ensure it is safe to prevent XSS vulnerabilities.
export default {
html: {
tags: [
// Result: <script>console.log("Hello, world!");</script>
{
tag: 'script',
children: 'console.log("Hello, world!");',
},
],
},
};boolean | string | ((url: string, hash: string) => string)falseControls whether to add a hash query parameter to asset URLs for cache invalidation, only affects the src attribute of the script tag and the href attribute of the link tag.
false: No hash querytrue: Generate hash based on HTML contentstring: Uses a custom hash stringfunction: Custom hash generation via a functionexport default {
html: {
tags: [
// Result: <script src="https://example.com/script.js?8327ec63"></script>
{
tag: 'script',
attrs: { src: 'https://example.com/script.js' },
hash: true,
},
],
},
};boolean | string | ((url: string, publicPath: string) => string)trueControls whether to prepend the asset prefix to resource URLs. Only affects the src attribute of the script tag and the href attribute of the link tag.
true: Prepends asset prefix to the URLfalse: Uses the URL as-isstring: Uses a custom prefixfunction: Custom path transformation via a functionexport default {
output: {
assetPrefix: 'https://cdn.example.com/',
},
html: {
tags: [
// Result: <script src="https://cdn.example.com/script.js"></script>
{
tag: 'script',
attrs: { src: '/script.js' },
publicPath: true,
},
],
},
};booleantrueControls whether to insert the tag after existing tags.
true: Insert after existing tagsfalse: Insert before existing tagsexport default {
html: {
tags: [
{
tag: 'script',
attrs: { src: 'https://example.com/script.js' },
append: false,
},
],
},
};See Injection position for more details.
booleanSpecifies whether to inject the tag into the HTML <head> element.
true: Inject into <head>false: Inject into <body>export default {
html: {
tags: [
{
tag: 'script',
attrs: { src: 'https://example.com/script.js' },
head: false,
},
],
},
};For elements allowed in the <head>, the default is true; for other elements, the default is false.
Allowed element types in the <head> include:
titlebaselinkstylemetascriptnoscripttemplateSee Injection position for more details.
Record<string, any>undefinedThe metadata object for tags, used to store additional information about tags. metadata does not affect the generated HTML content.
For example, Rsbuild plugins can add metadata to tags to identify their source; in subsequent processes, you can perform special handling on tags based on the metadata:
const myPlugin = {
name: 'my-plugin',
setup(api) {
api.modifyHTMLTags(({ headTags, bodyTags }) => {
headTags.push({
tag: 'script',
attrs: { src: 'https://example.com/script.js' },
metadata: {
from: 'my-plugin',
},
});
return { headTags, bodyTags };
});
},
};
export default {
plugins: [myPlugin],
html: {
tags: [
(tags) => {
return tags.map((tag) => {
if (tag.metadata?.from === 'my-plugin') {
return {
...tag,
// ...extra options
};
}
return tag;
});
},
],
},
};The final injection position of the tag is determined by the head and append options.
append: Defines the injection position of the current tag relative to existing tags, defaults to truehead: Specifies whether to add the current tag to the HTML <head> element, defaults to true for element types allowed in the <head>, otherwise false.head and append options, they will be inserted into the same area and hold their relative positions to each other.<html>
<head>
<!-- tags with `{ head: true, append: false }` here -->
<!-- existing head tags... -->
<!-- tags with `{ head: true, append: true }` here -->
</head>
<body>
<!-- tags with `{ head: false, append: false }` here -->
<!-- existing body tags... -->
<!-- tags with `{ head: false, append: true }` here -->
</body>
</html>type HtmlTagContext = {
hash: string;
entryName: string;
outputName: string;
publicPath: string;
};
type HtmlTagHandler = (
tags: HtmlTag[],
context: HtmlTagContext,
) => HtmlTag[] | void;html.tags can also accept functions that can arbitrarily modify tags by writing logic to the callback, often used to ensure the relative position of tags while inserting them.
The callback function accepts a tag list as an argument and needs to modify or return a new tag array directly.
export default {
html: {
tags: [
(tags) => [{ tag: 'script', attrs: { src: 'a.js' } }, ...tags],
(tags) => {
// Modify 'a.js' tag
const target = tags.find((tag) => tag.attrs?.src === 'a.js');
if (target) {
target.attrs ||= {};
target.attrs.defer = true;
}
},
(tags) => {
// Insert 'b.js' after 'a.js'
const targetIndex = tags.findIndex((tag) => tag.attrs?.src === 'a.js');
tags.splice(targetIndex + 1, 0, {
tag: 'script',
attrs: { src: 'd.js' },
});
},
],
},
};The HTML file will look like:
<html>
<head>
<script src="/a.js" defer></script>
<script src="/d.js"></script>
<!-- some other head tags... -->
</head>
<body>
<!-- some body tags... -->
</body>
</html>This configuration modifies the content of HTML files after Rsbuild completes building, and does not resolve or parse new modules. It cannot be used to import uncompiled source code files, nor can it replace configurations such as source.preEntry.
For example, for the following project:
web-app
├── src
│ ├── index.tsx
│ └── polyfill.ts
└── rsbuild.config.tsexport default {
output: {
assetPrefix: 'https://example.com/',
},
html: {
tags: [{ tag: 'script', attrs: { src: './src/polyfill.ts' } }],
},
};The tag object here will be directly added to the HTML file after processing, but the polyfill.ts will not be transpiled or bundled, so there will be a 404 error when processing this script in the application.
<body>
<script src="https://example.com/src/polyfill.ts"></script>
</body>Reasonable use cases include:
For example, the usage of the following example:
web-app
├── src
│ └── index.tsx
├── public
│ └── service-worker.js
└── rsbuild.config.tsfunction report() {
fetch('https://www.example.com/report');
}
export default {
html: {
output: {
assetPrefix: 'https://example.com/',
},
tags: [
// Inject asset from the `public` directory.
{ tag: 'script', attrs: { src: 'service-worker.js' } },
// Inject asset from other CDN url.
{
tag: 'script',
publicPath: false,
attrs: { src: 'https://cdn.example.com/foo.js' },
},
// Inject inline script.
{
tag: 'script',
children: report.toString() + '\nreport()',
},
],
},
};The result will look like:
<body>
<script src="https://example.com/service-worker.js"></script>
<script src="https://cdn.example.com/foo.js"></script>
<script>
function report() {
fetch('https://www.example.com/report');
}
report();
</script>
</body>| Version | Changes |
|---|---|
| v1.4.7 | Added support for metadata option |