Generate llms.txt related files for your Rspress site so large language models can better understand your documentation.
Warning
@rspress/plugin-llms uses remark to process MDX source files, so it does not support rendering dynamic content such as React Hooks or custom components.
This plugin is intended only as a fallback when SSG and SSG-MD cannot be enabled because the code is incompatible with SSR. Prefer the SSG-MD feature when possible.
Installation
npm add @rspress/plugin-llms -D
yarn add @rspress/plugin-llms -D
pnpm add @rspress/plugin-llms -D
bun add @rspress/plugin-llms -D
deno add npm:@rspress/plugin-llms -D
Usage
1. Install the plugin
Add the following configuration:
// rspress.config.ts
import { defineConfig } from '@rspress/core';
import { pluginLlms } from '@rspress/plugin-llms';
export default defineConfig({
plugins: [pluginLlms()],
});
Then run rspress build. During output generation, the plugin also generates llms.txt, llms-full.txt, and corresponding Markdown files for each route in the output directory, based on the navbar and sidebar.
2. UI display
To help readers use your documentation with large language models, add a copy Markdown button at the top of the page, similar to this website.
Using themeConfig (Recommended)
The simplest way is to enable llmsUI in themeConfig. This will automatically add LlmsCopyButton and LlmsViewOptions components below all H1 headers (or in the outline panel) without requiring custom theme code:
rspress.config.ts
import { defineConfig } from '@rspress/core';
import { pluginLlms } from '@rspress/plugin-llms';
export default defineConfig({
plugins: [pluginLlms()],
themeConfig: {
llmsUI: true,
// Or with custom options:
// llmsUI: {
// viewOptions: ['markdownLink', 'chatgpt', 'claude'],
// placement: 'outline', // Display in outline panel instead of below H1
// },
},
});
Using custom theme
If you need more control, you can use custom theme to add a copy Markdown button.
Add a copy button to all pages:
theme/index.tsx
import { getCustomMDXComponent as basicGetCustomMDXComponent } from '@rspress/core/theme-original';
import {
LlmsContainer,
LlmsCopyButton,
LlmsViewOptions,
} from '@rspress/plugin-llms/runtime';
function getCustomMDXComponent() {
const { h1: H1, ...mdxComponents } = basicGetCustomMDXComponent();
const MyH1 = ({ ...props }) => {
return (
<>
<H1 {...props} />
<LlmsContainer>
<LlmsCopyButton />
{/* Add LlmsViewOptions as needed */}
<LlmsViewOptions />
</LlmsContainer>
</>
);
};
return {
...mdxComponents,
h1: MyH1,
};
}
export { getCustomMDXComponent };
export * from '@rspress/core/theme-original';
Add a copy button to specific pages:
docs/hello-world.mdx
# Hello world
<LlmsContainer>
<LlmsCopyButton />
<LlmsViewOptions /> {/* Add LlmsViewOptions as needed */}
</LlmsContainer>
This is a sample document.
Configuration
This plugin accepts an options object with the following type:
interface LlmsTxt {
name: string;
onTitleGenerate?: (context: {
title: string | undefined;
description: string | undefined;
}) => string;
onLineGenerate?: (page: PageIndexInfo) => string;
onAfterLlmsTxtGenerate?: (llmsTxtContent: string) => string;
}
interface MdFiles {
mdxToMd?: boolean;
remarkPlugins?: PluggableList;
}
interface LlmsFullTxt {
name: string;
}
export interface Options {
llmsTxt?: false | LlmsTxt;
mdFiles?: false | MdFiles;
llmsFullTxt?: false | LlmsFullTxt;
include?: (context: { page: PageIndexInfo }) => boolean;
exclude?: (context: { page: PageIndexInfo }) => boolean;
}
When internationalization is not enabled, the default value is:
{
llmsTxt: { name: 'llms.txt' },
llmsFullTxt: { name: 'llms-full.txt' },
mdFiles: true
}
When internationalization is enabled, it will use multiple configurations, with the default value being:
[
{
llmsTxt: { name: 'llms.txt' },
llmsFullTxt: { name: 'llms-full.txt' },
mdFiles: true,
include: ({ page }) => page.lang === config.lang,
},
// Automatically generate other languages based on locales configuration
{
llmsTxt: { name: `${lang}/llms.txt` },
llmsFullTxt: { name: `${lang}/llms-full.txt` },
mdFiles: true,
include: ({ page }) => page.lang === lang,
},
// ...
];
llmsTxt
import type { PageIndexInfo } from '@rspress/core';
export interface LlmsTxt {
name: string;
onTitleGenerate?: (context: {
title: string | undefined;
description: string | undefined;
}) => string;
onLineGenerate?: (page: PageIndexInfo) => string;
onAfterLlmsTxtGenerate?: (llmsTxtContent: string) => string;
}
- Default:
{ name: 'llms.txt' }
Controls whether to generate the llms.txt file, or customizes it through hooks.
The default format of an llms.txt file is as follows:
# {title}
> {description}
## {nav1.title}
- [{page.title}]({ page.routePath }): {page.frontmatter.description}
## {nav2.title}
- [{page.title}]({ page.routePath }): {page.frontmatter.description}
You can modify specific parts through hooks:
onTitleGenerate: Customize the generated title and description sections.
onLineGenerate: Customize each line of the Markdown file.
onAfterLlmsTxtGenerate: Modify the final contents of the llms.txt file.
For example:
pluginLlms({
llmsTxt: {
onTitleGenerate: ({ title, description }) => {
return `# ${title} - llms.txt
> ${description}
Rspress is a static site generator based on Rsbuild and it can generate llms.txt with @rspress/plugin-llms.
`;
},
},
});
The generated result is:
# Rspress - llms.txt
> Rsbuild based static site generator
Rspress is a static site generator based on Rsbuild and it can generate llms.txt with @rspress/plugin-llms.
## guide
- [foo](/foo.md)
mdFiles
export interface MdFiles {
mdxToMd?: boolean;
remarkPlugins?: PluggableList;
}
- Default:
{ mdxToMd: false, remarkPlugins: [] }
Controls whether to generate a Markdown file for the corresponding route. When set to false, the Markdown file for that route is not generated.
mdxToMd
- Type:
boolean
- Default:
false
Controls whether to convert MDX content to Markdown. If enabled, MDX files are converted to Markdown files through a set of default strategies, but some information may be lost.
- Type:
PluggableList
- Default:
[]
You can pass in custom remark plugins to modify the Markdown content.
llmsFullTxt
- Type:
false | LlmsFullTxt
export interface LlmsFullTxt {
name: string;
}
- Default:
{ name: 'llms-full.txt' }
Controls whether to generate llms-full.txt. When set to false, llms-full.txt is not generated.
include
- Type:
(context: { page: PageIndexInfo }) => boolean
Controls which pages are included during generation. This is usually used to simplify llms.txt.
Generate llms.txt and other related files for pages whose language is English only:
pluginLlms({
llmsTxt: {
name: 'llms.txt',
},
llmsFullTxt: {
name: 'llms-full.txt',
},
include: ({ page }) => {
return page.lang === 'en';
},
});
exclude
- Type:
(context: { page: PageIndexInfo }) => boolean
Controls which pages are excluded. This runs after include.
Exclude a single page under the /foo route:
pluginLlms({
llmsTxt: {
name: 'llms.txt',
},
llmsFullTxt: {
name: 'llms-full.txt',
},
exclude: ({ page }) => {
return page.routePath === '/foo';
},
});
UI component props
- Type:
LlmsCopyButtonProps
interface LlmsCopyButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
LlmsViewOptionsProps
- Type:
LlmsViewOptionsProps
type LlmsViewOptionsItem =
| {
title: string;
icon?: React.ReactNode;
onClick?: () => void;
}
| {
title: string;
href: string;
icon?: React.ReactNode;
}
| 'markdownLink'
| 'chatgpt'
| 'claude';
interface LlmsViewOptionsProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
options?: LlmsViewOptionsItem[];
}
options
- Type:
LlmsViewOptionsItem[]
type LlmsViewOptionsItem =
| {
title: string;
icon?: React.ReactNode;
onClick?: () => void;
}
| {
title: string;
href: string;
icon?: React.ReactNode;
}
| 'markdownLink'
| 'chatgpt'
| 'claude';
- Default:
['markdownLink', 'chatgpt', 'claude']
Customizes the options in the dropdown menu. By default, it supports "Copy Markdown Link", ChatGPT, and Claude.
Generate multiple groups of llms.txt at the same time
In some cases, such as i18n sites, you may need to generate multiple llms.txt groups. Pass an array to do this.
// rspress.config.ts
import { defineConfig } from '@rspress/core';
defineConfig({
lang: 'en',
plugins: [
pluginLlms([
{
llmsTxt: {
name: 'llms.txt',
},
llmsFullTxt: {
name: 'llms-full.txt',
},
include: ({ page }) => page.lang === 'en',
},
{
llmsTxt: {
name: 'zh/llms.txt',
},
llmsFullTxt: {
name: 'zh/llms-full.txt',
},
include: ({ page }) => page.lang === 'zh',
},
]),
],
});