# docusaurus-plugin-llms > llms.txt and Markdown copies of your Docusaurus docs This file contains all documentation content in a single document following the llmstxt.org standard. ## Overview `docusaurus-plugin-llms` writes text files that describe your Docusaurus docs for language models, in the format the [llmstxt standard](https://llmstxt.org/) defines. During a production build it writes an `llms.txt` file with links to every documentation page and an `llms-full.txt` file that bundles all your content into a single document, so LLMs can read your docs without parsing HTML. The plugin needs no configuration. It runs in the Docusaurus `postBuild` hook, so it generates files during `npm run build` and not during `docusaurus start`. ### What it does - Generates `llms.txt` with links to each documentation page. - Generates `llms-full.txt` with all content in one file. - Optionally generates individual `.md` files per page, which the llmstxt.org spec recommends. - Controls document order with glob patterns. - Includes blog posts when you enable them. - Generates custom LLM files scoped to specific sections, languages, or content types. - Cleans content for LLMs: strips HTML, removes import statements, and removes duplicate headings. - Rewrites relative image paths to absolute build-output URLs. - Supports multi-instance and multi-version docs setups. - Reports statistics about the generated output. ### Generated output With the defaults, the plugin scans the `docs` directory and writes two files to the build output: ``` build/ ├── llms.txt # Links to each documentation page └── llms-full.txt # All documentation content in one file ``` The generated `llms.txt` opens with your site title and tagline, followed by a link to each page and that page's description: ``` # My Site > My site tagline This file contains links to documentation sections following the llmstxt.org standard. ## Table of Contents - [Getting Started](https://example.com/docs/getting-started): Install the CLI and run your first build. - [API Reference](https://example.com/docs/api/reference): Every endpoint, with request and response examples. ``` Links point at your HTML pages. To link to a Markdown copy of each page, as the llmstxt.org spec recommends, see [generating individual Markdown files](./markdown-files.md). In `llms-full.txt`, each page appears under a `## {title}` heading, with the page's own headings shifted down one level so they nest under it. The plugin skips pages with `draft: true` in their front matter. ### Page titles and descriptions Each entry takes its title and description from the source page. The title comes from the front matter `title`, then the first `#` heading, then the file name. The description comes from the first of these that exists: 1. The front matter `description`. 2. The first prose paragraph. Code blocks, tables, admonitions, JSX or HTML elements, images, and `import`/`export` lines are skipped. 3. The text of the first heading. `llms.txt` shows the first line of the description, truncated to 150 characters. For a summary you control, set `description` in each page's front matter: ```markdown --- description: Install the CLI and run your first build. --- ``` ### Translated sites On a site with [Docusaurus i18n](https://docusaurus.io/docs/i18n/introduction), each locale's build writes its own files to that locale's build directory, such as `build/fr/llms.txt`, with links under the locale's URLs. A page's content comes from its translation in `i18n//docusaurus-plugin-content-docs/` (or `i18n//docusaurus-plugin-content-blog/` for blog posts). A page with no translation uses its source file, as the Docusaurus build does. A locale with `translate: false` always uses the source files. ### Next steps - See [installation](./installation.md) to add the plugin to your project. - See [configuration options](./configuration.md) for the full list of options, including document ordering, custom LLM files, content cleaning, and multi-version output. --- ## Installation Install the package, register it in `docusaurus.config.js`, and run a production build to generate `llms.txt` and `llms-full.txt`. ### Install the package Install the plugin as a dev dependency with your package manager. With npm: ```bash npm install docusaurus-plugin-llms --save-dev ``` With yarn: ```bash yarn add --dev docusaurus-plugin-llms ``` With pnpm: ```bash pnpm add --save-dev docusaurus-plugin-llms ``` ### Register the plugin Add the plugin to the `plugins` array in your `docusaurus.config.js`: ```js module.exports = { // ... your existing Docusaurus config plugins: [ 'docusaurus-plugin-llms', // ... your other plugins ], }; ``` With no options, the plugin reads from the `docs` directory and generates both `llms.txt` (a links file) and `llms-full.txt` (all content in one file). To change what it generates, see [configuration options](./configuration.md). ### Verify it works The plugin runs in the `postBuild` lifecycle hook, so it generates files during a production build. Build your site: ```bash npm run build ``` When the build finishes, check that `llms.txt` exists in the build output: ```bash cat build/llms.txt ``` The file starts with your site title and tagline, followed by a link to each documentation page. `build/llms-full.txt` holds the full content of every page. To control the description shown next to each link, see [page titles and descriptions](./overview.md#page-titles-and-descriptions). --- ## Configuration options This page lists every option `docusaurus-plugin-llms` accepts, with its type and default. The plugin runs with no options; set these to change its defaults. To set the plugin up for the first time, see [installation](./installation.md). You pass options as the second element of the plugin tuple in `docusaurus.config.js`: ```js module.exports = { plugins: [ [ 'docusaurus-plugin-llms', { generateLLMsTxt: true, docsDir: 'docs', ignoreFiles: ['advanced/*', 'private/*'], title: 'My Project Documentation', description: 'Complete reference documentation for My Project', }, ], ], }; ``` ### Output files These options control which files the plugin writes and what they're named. | Option | Type | Default | Description | | ---------------------------- | ---------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `generateLLMsTxt` | `boolean` | `true` | Whether to generate the `llms.txt` links file. | | `generateLLMsFullTxt` | `boolean` | `true` | Whether to generate the `llms-full.txt` full-content file. | | `llmsTxtFilename` | `string` | `'llms.txt'` | Custom file name for the links file. | | `llmsFullTxtFilename` | `string` | `'llms-full.txt'` | Custom file name for the full-content file. | | `generateMarkdownFiles` | `boolean` | `false` | Generate an individual `.md` file per page and link to those from `llms.txt` in place of the original docs. | | `preserveDirectoryStructure` | `boolean` | `true` | Preserve the source directory structure in generated markdown files (for example `docs/server/config.md`). Only applies when `generateMarkdownFiles` is `true`. | | `keepFrontMatter` | `string[]` | none | Front matter keys to preserve in generated markdown files. Only applies when `generateMarkdownFiles` is `true`. | ### Content sources These options select which files the plugin reads and in what order. | Option | Type | Default | Description | | ---------------------- | ------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `docsDir` | `string \| DocsSection[]` | `'docs'` | Documentation source. A string base directory, or an array of section objects for multi-instance setups. See [Documentation sections](#documentation-sections). | | `ignoreFiles` | `string[]` | `[]` | Glob patterns for files to skip. | | `includeBlog` | `boolean` | `false` | Whether to include blog posts. See [include blog posts](content-generation.md#include-blog-posts). | | `blogDir` | `string` | `'blog'` | Filesystem path to the blog content directory, relative to the site root. | | `blogRouteBasePath` | `string` | `'blog'` | Docusaurus `routeBasePath` for the blog plugin, relative to the site `baseUrl`. Set this to match `presets.blog.routeBasePath` if you've customized it (for example `'news'`). | | `includeOrder` | `string[]` | `[]` | Glob patterns controlling the order files are processed in. | | `includeUnmatchedLast` | `boolean` | `true` | Whether to append files that match no `includeOrder` pattern at the end. Set to `false` to make `includeOrder` a strict inclusion list. | | `processingBatchSize` | `number` | `100` | Number of documents per batch when assembling `llms-full.txt`. Sets how often verbose mode logs progress. See [batch processing](ordering-and-paths.md#batch-processing-for-large-sites). | ### Content and metadata These options set the title, description, and inline content of the generated files. | Option | Type | Default | Description | | ----------------- | -------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `title` | `string` | Site title | Custom title used in generated files. | | `description` | `string` | Site tagline | Custom description used in generated files. | | `version` | `string` | `undefined` | Global version label stamped into all generated files. | | `rootContent` | `string` | Standard llmstxt.org blurb | Custom content inserted at the root of `llms.txt`, after the title and description and before the table of contents. | | `fullRootContent` | `string` | Standard llmstxt.org blurb | Custom content inserted at the root of `llms-full.txt`, after the title and description and before the content sections. | ### Content cleaning These options remove markup from the source Markdown before the plugin writes it. | Option | Type | Default | Description | | ------------------------- | ---------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `excludeImports` | `boolean` | `false` | Remove `import` statements from generated content. | | `removeDuplicateHeadings` | `boolean` | `false` | Remove a line that repeats the heading text directly below the heading. | | `preserveComponents` | `string[]` | `[]` | Component names whose tags and props the plugin keeps as written. See [preserving component tags](content-cleaning.md#preserving-component-tags-preservecomponents). | ### URL construction These options control the links the plugin emits. | Option | Type | Default | Description | | -------------------- | --------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `addMdExtension` | `boolean` | `true` | Append `.md` to link URLs in `llms.txt`, per the llmstxt.org spec. Only applies when `generateMarkdownFiles` is `true`. | | `useRelativeUrls` | `boolean` | `false` | Write links as paths from the site root (for example `/docs/page.md`). See [relative links](ordering-and-paths.md#relative-links). | | `rewriteImageUrls` | `boolean` | `false` | Rewrite relative image references to absolute hashed build-output URLs so LLMs can resolve them. | | `pathTransformation` | `object` | `undefined` | Fallback path rewriting for URL construction. See [Path transformation](#path-transformation). | The `pathTransformation` object accepts these fields: | Field | Type | Default | Description | | ------------- | ---------- | ------- | ------------------------------------------------------------------------ | | `ignorePaths` | `string[]` | `[]` | Path segments to remove when constructing URLs. | | `addPaths` | `string[]` | `[]` | Path segments to prepend when constructing URLs, if not already present. | ### Advanced These options add extra files, split output by version, and control logging. | Option | Type | Default | Description | | -------------------- | ---------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customLLMFiles` | `CustomLLMFile[]` | `[]` | Additional LLM files for specific documentation sections. See [Custom LLM files](#custom-llm-files). | | `versions` | `VersionConfig[] \| 'auto'` | `undefined` | Generate a version-scoped set of files per documentation version. `'auto'` detects versions from Docusaurus docs versioning. See [Versions](#versions). | | `logLevel` | `'quiet' \| 'normal' \| 'verbose'` | `'normal'` | Amount of build output the plugin prints. See [Logging](#logging). | | `warnOnIgnoredFiles` | `boolean` | `false` | Whether to warn about files that are skipped because they have no extension or an unsupported one. | ### Documentation sections `docsDir` accepts either a string (a single base directory) or an array of `DocsSection` objects. Use the array form for multi-instance setups, so each section resolves against its own route base path and can carry its own heading in `llms.txt`. Each section object has this shape: | Field | Type | Required | Description | | --------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------- | | `path` | `string` | yes | Filesystem path to the section, relative to the site directory (for example `'docs'`, `'api'`). | | `routeBasePath` | `string` | yes | The Docusaurus `routeBasePath` the section is served under (for example `'docs'`, `'api'`), relative to the site `baseUrl`. | | `label` | `string` | no | Heading for the section in links files such as `llms.txt`. Defaults to the section's `path`. | Section headings appear when `docsDir` lists two or more sections. `llms-full.txt` has no section headings. When `docsDir` is a string, the plugin treats it as a single section and matches its files against all of the site's routes. ### Custom LLM files Each entry in `customLLMFiles` is a `CustomLLMFile` object with these fields: | Field | Type | Required | Description | | ---------------------- | ---------- | -------- | ------------------------------------------------------------------------------------------- | | `filename` | `string` | yes | Name of the output file (for example `'llms-python.txt'`). | | `includePatterns` | `string[]` | yes | Glob patterns for files to include. | | `fullContent` | `boolean` | yes | `true` for full content like `llms-full.txt`, `false` for links only like `llms.txt`. | | `title` | `string` | no | Custom title for this file. Defaults to the site title. | | `description` | `string` | no | Custom description for this file. Defaults to the site description. | | `ignorePatterns` | `string[]` | no | Additional patterns to exclude, combined with the global `ignoreFiles`. | | `orderPatterns` | `string[]` | no | Order patterns for this file, like `includeOrder`. | | `includeUnmatchedLast` | `boolean` | no | Whether to append files that match no `orderPatterns` entry at the end. Defaults to `true`. | | `version` | `string` | no | Version label for this file, overriding the global `version`. | | `rootContent` | `string` | no | Custom content inserted at the root of this file, after the title and description. | ### Versions Set `versions` to publish a separate set of LLM files per documentation version, each written under its own subdirectory with links scoped to that version's routes. Provide an explicit array of `VersionConfig` objects, or `'auto'` to detect versions, their routes, and the last version from the Docusaurus docs plugin config and `versions.json`. Any field left unset on a version falls back to the matching top-level option. Each `VersionConfig` entry accepts: | Field | Type | Default | Description | | ---------------- | ------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | `string` | required | Version identifier (for example `'nightly'`, `'stable'`, `'0.0.1'`). | | `label` | `string` | `name` | Label written into the `Version:` line of generated files. With `versions: 'auto'`, see [automatic detection](multi-version.md#automatic-detection). | | `docsDir` | `string \| DocsSection[]` | top-level `docsDir` | Source docs directory or sections for this version. | | `path` | `string` | `name` | Output subdirectory and route prefix. Use `''` for the site root. | | `routePrefix` | `string` | `path` | Route prefix the version's links resolve under. Use `''` when the sections' `routeBasePath` holds it. See [multi-version output](multi-version.md#versions-inside-the-route-base-path). | | `customLLMFiles` | `CustomLLMFile[]` | top-level value | Per-version custom LLM files. | | `includeOrder` | `string[]` | top-level value | Per-version include order. | ### Path transformation The plugin resolves URLs by matching file paths against the routes Docusaurus built. `pathTransformation` applies only to files that match no known route, so most sites don't need it. `ignorePaths` removes segments from the constructed URL and `addPaths` adds them: ```js pathTransformation: { ignorePaths: ['docs'], addPaths: ['api'], } ``` With `docsDir: 'docs'` and that config, the file `docs/manual/decorators.md` resolves to `https://example.com/api/manual/decorators`. For each field on its own, see [path transformation](ordering-and-paths.md#path-transformation). ### Logging The `logLevel` option controls how much the plugin prints during the build. It takes these values: - `'quiet'`: prints errors only. Use it to keep CI logs short. - `'normal'` (default): shows standard progress messages and warnings. - `'verbose'`: shows file-by-file processing detail. Use it for debugging. Set the level alongside your other options: ```js { logLevel: 'verbose', } ``` In `'normal'` mode, output looks like this: ``` [docusaurus-plugin-llms] Generating LLM-friendly documentation... [docusaurus-plugin-llms] Generated: /path/to/build/llms.txt [docusaurus-plugin-llms] Generated: /path/to/build/llms-full.txt [docusaurus-plugin-llms] No custom LLM files configured. Skipping. [docusaurus-plugin-llms] Stats: 42 documents processed ``` In `'quiet'` mode, only errors appear: ``` [docusaurus-plugin-llms] ERROR: Error generating LLM documentation: ... ``` --- ## Generating content These options control what the generated files contain: multiple documentation sections, custom root content, extra custom LLM files, and Docusaurus partials. To install the plugin first, see [installation](./installation.md). ### Configure multiple documentation sections The `docsDir` option accepts either a `string` (a single base directory, default `'docs'`) or an array of `DocsSection` objects. Use the array form for multi-instance setups, for example a main `docs` instance alongside a separate `api` instance. Each section resolves against its own route base path and gets its own heading in `llms.txt`. To generate a single `llms.txt` from two instances with distinct headings, pass an array: ```js module.exports = { plugins: [ [ 'docusaurus-plugin-llms', { docsDir: [ { path: 'docs', routeBasePath: 'docs', label: 'Guides' }, { path: 'api', routeBasePath: 'api', label: 'API Reference' }, ], }, ], ], }; ``` Set `routeBasePath` to the value in the matching Docusaurus docs plugin config, without the site `baseUrl`. A section with no `label` uses its `path` as the heading. For every `DocsSection` field, see [documentation sections](./configuration.md#documentation-sections). ### Inject custom root content The llmstxt.org standard allows Markdown content without headings (paragraphs, lists, and so on) between the title and description and the body. The `rootContent` and `fullRootContent` options, both of type `string`, set that introductory content. - `rootContent` is inserted into `llms.txt`, after the title and description and before the table of contents. - `fullRootContent` is inserted into `llms-full.txt`, after the title and description and before the content sections. If you don't set them, the plugin uses these defaults: - `llms.txt`: `This file contains links to documentation sections following the llmstxt.org standard.` - `llms-full.txt`: `This file contains all documentation content in a single document following the llmstxt.org standard.` To add project-specific context to `llms.txt`, set `rootContent` to a Markdown string: ```js { rootContent: `Welcome to the MyProject documentation. This documentation covers: - Installation and setup - API reference - Advanced usage guides - Troubleshooting For the latest updates, visit https://myproject.dev/changelog`, } ``` Because these values are plain JavaScript strings, you can compute them at build time, for example to stamp in a generation timestamp: ```js { fullRootContent: `MyProject v2.0 documentation, all pages in one file. **Format**: Markdown with code examples **Last Generated**: ${new Date().toISOString()} > Note: Some features require authentication tokens.`, } ``` Custom LLM files accept their own `rootContent` too. See the next section. ### Generate custom LLM files The `customLLMFiles` option generates extra LLM files, each scoped to part of your docs, alongside `llms.txt` and `llms-full.txt`. It takes an array of `CustomLLMFile` objects (default `[]`). Each object needs a `filename`, an `includePatterns` array of globs, and a `fullContent` flag: `true` writes full content like `llms-full.txt`, `false` writes links like `llms.txt`. For the optional fields, including `ignorePatterns`, `orderPatterns`, and `version`, see [custom LLM files](./configuration.md#custom-llm-files). To split documentation by programming language, define one file per language: ```js { customLLMFiles: [ { filename: 'llms-python.txt', includePatterns: ['api/python/**/*.md', 'guides/python/*.md'], fullContent: true, title: 'Python API Documentation', description: 'Python API reference and Python guides', }, { filename: 'llms-tutorials.txt', includePatterns: ['tutorials/**/*.md'], fullContent: false, title: 'Tutorial Documentation', description: 'Links to every tutorial', }, ], } ``` The `includePatterns`, `orderPatterns`, and `ignorePatterns` globs match against both the site-relative path (for example `docs/quickstart/file.md`) and the docs-relative path (for example `quickstart/file.md`). Docs-relative patterns keep working if `docsDir` changes. To set the page order of a custom file and exclude some pages, combine `ignorePatterns` and `orderPatterns`: ```js { customLLMFiles: [ { filename: 'llms-getting-started.txt', includePatterns: ['**/*.md'], ignorePatterns: ['advanced/**/*.md', 'internal/**/*.md'], orderPatterns: [ 'introduction.md', 'getting-started/*.md', 'tutorials/basic/*.md', ], fullContent: true, title: 'Getting Started Guide', description: 'Introduction, setup, and basic tutorials', }, ], } ``` You can also give a custom file its own introductory text with `rootContent`: ```js { customLLMFiles: [ { filename: 'llms-api.txt', includePatterns: ['api/**/*.md'], fullContent: true, title: 'API Documentation', rootContent: `Reference for the REST API endpoints. Authentication required for all endpoints except /health. Base URL: https://api.example.com/v2`, }, ], } ``` Set a per-file `version` to override the global `version` label. When present, the version appears on a `Version:` line under the description: ``` # API Reference Documentation > Endpoints, parameters, and response formats Version: 1.0.0 This file contains all documentation content in a single document following the llmstxt.org standard. ``` ### Include blog posts With `includeBlog: true`, the plugin adds the posts in the blog directory to `llms.txt` and `llms-full.txt`. `blogDir` (default `'blog'`) is the blog's folder, relative to the site directory, and `blogRouteBasePath` (default `'blog'`) is the blog plugin's `routeBasePath`, relative to the site `baseUrl`. Set both to match your blog plugin config: ```js { includeBlog: true, blogDir: 'news', blogRouteBasePath: 'news', } ``` Each post links to the route Docusaurus builds for it: - A post named with a date, such as `2024-01-01-hello.md` or `2024-01-01-hello/index.md`, links to `/blog/2024/01/01/hello`. - A post with a front matter `slug` links to that slug under the blog route, such as `/blog/my-custom`. - Posts with `draft: true` are skipped. `ignoreFiles` and `includeOrder` patterns match blog posts by their site-relative path, such as `blog/2024-01-01-hello.md`. When `docsDir` lists several sections, the posts appear under a `## Blog` heading in `llms.txt`. In versions mode, posts appear in the version written at the site root only. ### How partials are resolved The plugin supports [Docusaurus partials](https://docusaurus.io/docs/markdown-features/react#importing-markdown), the reusable MDX files imported into other documents. Partials are handled with no extra configuration: - Files whose name starts with an underscore (for example `_shared-config.mdx`) are partials. The plugin skips them when it collects pages, so they appear only where another page imports them. - Every import of a `.md` or `.mdx` file is resolved, and the imported content is inlined into the importing document before it's processed. Import paths that start with `@site/` resolve against the site directory. Other `@alias/` paths resolve against the site directory too, as `/alias/...`. Relative paths resolve against the importing file. The plugin reports a circular import as an error and drops that import. Given a partial file `_api-config.mdx`: ````mdx ## API Configuration Set your API endpoint: ```javascript const API_URL = 'https://api.example.com'; ``` ```` And a document that imports it: ```mdx --- title: Getting Started --- # Getting Started Guide import ApiConfig from './_api-config.mdx'; Now you can make API calls... ``` The plugin excludes `_api-config.mdx` from `llms.txt` and replaces both the `import` statement and the `` tag with the partial's content in the processed document. --- ## Generating individual Markdown files The [llmstxt.org specification](https://llmstxt.org/) recommends that `llms.txt` link to a Markdown version of each page. With `generateMarkdownFiles: true`, the plugin writes a `.md` file for every document and points the `llms.txt` links at those files. This page covers how the plugin names and lays out the files, what each file contains, and the options that set file names and locations. For the format of `llms.txt` itself, see [generated output](./overview.md#generated-output). For content cleaning options that also apply here, see [content cleaning](./content-cleaning.md). ### What `generateMarkdownFiles` does `generateMarkdownFiles` is a `boolean` that defaults to `false`. When you leave it off, `llms.txt` links to your HTML pages: ``` - [Getting Started](https://yoursite.com/docs/getting-started) ``` When you turn it on, the plugin writes an individual Markdown file for each document and links to that file instead: ``` - [Getting Started](https://yoursite.com/docs/getting-started.md) ``` Each generated file holds the page's Markdown after content cleaning. A minimal configuration looks like this: ```js module.exports = { plugins: [ [ 'docusaurus-plugin-llms', { generateMarkdownFiles: true, // write individual .md files generateLLMsTxt: true, // index file that links to them excludeImports: true, // strip MDX import statements removeDuplicateHeadings: true, // drop lines that repeat a heading includeOrder: ['getting-started/*', 'guides/*', 'api/*'], }, ], ], }; ``` `generateMarkdownFiles` works alongside the other options. Content cleaning (`excludeImports`, `removeDuplicateHeadings`), ordering (`includeOrder`), path transformation, and `customLLMFiles` all apply to the generated files. ### Linking with `addMdExtension` `addMdExtension` is a `boolean` that defaults to `true`. It appends `.md` to the link URLs in `llms.txt` so they resolve to the generated Markdown files. It applies only when `generateMarkdownFiles` is `true`, because the `.md` files must exist for the links to resolve. A Docusaurus page served at `https://example.com/docs/getting-started/` becomes this link: ``` - [Getting Started](https://example.com/docs/getting-started.md) ``` The plugin strips trailing slashes before it adds the extension, and leaves a URL that already ends in `.md` as it is. To keep the Docusaurus page URLs in `llms.txt` while still generating the files, set the option to `false`: ```js { addMdExtension: false, } ``` ### Where files are written with `preserveDirectoryStructure` `preserveDirectoryStructure` is a `boolean` that defaults to `true`. It controls whether generated files mirror the directory layout of your build output or are flattened relative to the docs root. With the default (`true`), a file keeps its directory path so it can sit next to the matching HTML file and be served from the same URL with a `.md` extension: ``` docs/server/config.md → build/docs/server/config.md ``` With `preserveDirectoryStructure: false`, the leading docs directory segment is dropped and the file is flattened: ``` docs/server/config.md → build/server/config.md ``` With the default, a page served at `https://yoursite.com/docs/server/config` gets its Markdown at `https://yoursite.com/docs/server/config.md`. ### Generated file structure With `generateMarkdownFiles: true` and the default `preserveDirectoryStructure: true`, the build output contains the index files plus a Markdown tree that mirrors your docs: ``` build/ ├── llms.txt # index file linking to the generated markdown files ├── llms-full.txt # full content file (if generateLLMsFullTxt is enabled) ├── docs/ # directory structure preserved (default) │ ├── getting-started.md │ ├── api/ │ │ └── reference.md │ └── server/ │ └── config.md └── ... ``` With `preserveDirectoryStructure: false`, the same files land flattened relative to the docs root: ``` build/ ├── llms.txt ├── llms-full.txt ├── getting-started.md ├── api/ │ └── reference.md └── server/ └── config.md ``` ### How filenames are chosen The plugin derives each output path from the document's resolved information, in this order: 1. **Front matter `slug`**: if the document sets a `slug`, it becomes the output path. A slug containing `/` creates the matching directory structure; a slug without `/` replaces only the filename. 2. **Front matter `id`**: if there's no `slug` but there is an `id`, it's used the same way. 3. **Resolved page URL**: otherwise the path comes from the document's built URL (or its source file path if the URL isn't available). Numeric ordering prefixes like `01-` are stripped from each path segment. A directory index (`index.md`, `README.md`, or a `/.md` file, in any letter case) takes its directory's URL: `docs/guide/README.md` is served at `/docs/guide/`, so its file is `docs/guide.md`. 4. **Sanitized title**: when the path would otherwise be empty, the document title is turned into a filename (lowercased, unsafe characters and whitespace replaced with `-`). When two documents would get the same path, the plugin appends a counter (`config-2.md`, `config-3.md`, and so on). A page whose URL is the site root is written as `index.md`. ### What each file contains Every generated file is built from the document's title, description, and processed content: - The **title** becomes an H1 heading. - The **description**, when present, follows as a blockquote, matching the llmstxt.org format. For where the description comes from, see [page titles and descriptions](./overview.md#page-titles-and-descriptions). - The **processed content** comes next, with content cleaning applied. A body paragraph that repeats the description is dropped. If you list keys in `keepFrontMatter`, the plugin writes a YAML front matter block at the top of each file containing only those keys. `keepFrontMatter` is a `string[]` that defaults to `[]`, and it only takes effect when `generateMarkdownFiles` is enabled. Keys you don't list are dropped. ### Example generated file A document titled "API Authentication" with a description produces `api-authentication.md` (or a path derived from its slug or URL): ```markdown # API Authentication > Authenticate API requests with an API key or an OAuth token. ## Overview Send the API key in the `Authorization` header of each request... ## API Key Authentication Use your API key to authenticate requests: const client = new Client({ apiKey: 'your-key' }); ``` ### Generate only `llms.txt` and the Markdown files To skip the combined `llms-full.txt`, turn it off and keep the index and the individual files: ```js { generateMarkdownFiles: true, generateLLMsTxt: true, generateLLMsFullTxt: false, } ``` --- ## Content cleaning A language model reads MDX and HTML markup as text. Before it writes `llms-full.txt` and any individual Markdown files, the plugin removes MDX import statements, HTML tags, and auto-generated lines that repeat their heading. Some cleaning always runs. The plugin removes common HTML tags (`
`, ``, ``, and so on) and MDX/JSX component tags (PascalCase elements like `` or ``), and keeps their inner text. Docusaurus's `` is a special case: the plugin writes its `label` (or `value`, when no label is set) as a bold line before the tab body, so tabbed sections keep their structure. To keep other component tags, list them in [`preserveComponents`](#preserving-component-tags-preservecomponents). The other cleaning options are opt-in, and each defaults to `false`: `excludeImports`, `removeDuplicateHeadings`, and `rewriteImageUrls`. Cleaning skips fenced code and inline code, so an `import` line or an HTML snippet inside a code sample is left as written. ### Import statement removal (`excludeImports`) `excludeImports` is a `boolean` (default `false`). When `true`, it removes JavaScript and TypeScript `import` statements from your MDX content. An API reference page that uses many theme components can start with a dozen of these lines. The option strips the common `import` forms: named imports, default imports, namespace imports (`import * as ...`), and side-effect imports (`import "...";`). Given an MDX file that starts with a block of component imports: ```markdown import ApiTabs from "@theme/ApiTabs"; import DiscriminatorTabs from "@theme/DiscriminatorTabs"; import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import MimeTabs from "@theme/MimeTabs"; # Create user account This endpoint creates a new user account... ``` With `excludeImports: true`, the generated output drops the import lines and keeps the content: ```markdown # Create user account This endpoint creates a new user account... ``` To enable it: ```js { excludeImports: true, } ``` Import lines inside a fenced code block are left alone. Imports of [Docusaurus partials](https://docusaurus.io/docs/markdown-features/react#importing-markdown) are resolved and inlined whether or not this option is set; see [how partials are resolved](./content-generation.md#how-partials-are-resolved). ### Duplicate heading removal (`removeDuplicateHeadings`) `removeDuplicateHeadings` is a `boolean` (default `false`). When `true`, it removes a line that repeats its heading text immediately below the heading. This pattern is common in auto-generated API docs, where each entry renders both a heading and a body line containing the same text. The plugin drops the next non-empty line after a heading only when that line matches the heading text and is itself plain text, not a heading. Blank lines between the heading and the repeated text are preserved, and a lower-level heading of the same wording is never removed. Given a file where each entry duplicates its title: ```markdown # Create deliverable Create deliverable --- # Update user profile Update user profile --- ``` With `removeDuplicateHeadings: true`, the plugin removes the repeated lines: ```markdown # Create deliverable --- # Update user profile --- ``` To enable it: ```js { removeDuplicateHeadings: true, } ``` ### Preserving component tags (`preserveComponents`) `preserveComponents` is a `string[]` (default `[]`). By default the plugin strips every MDX/JSX component tag (PascalCase elements) and keeps only the inner text. Some components carry the page content in their props, such as a swizzled `` that renders install commands or a custom `` that renders download steps. Stripping those tags removes that content. List the component names, and the plugin keeps their tags and props as written: ```js module.exports = { plugins: [ [ 'docusaurus-plugin-llms', { preserveComponents: ['PackageManagerTabs', 'ModelDownload'], }, ], ], }; ``` Given MDX like: ```markdown ``` The generated output keeps the tag, including the `command` prop the component renders. The plugin compares the full component name in opening and closing tags, so listing `Keep` preserves `` and still strips ``. Two special cases run regardless of this option: - `` is never preserved as a raw tag. Instead, its `label` (or `value` when no label is set) is emitted as a bold line before the tab body, so a tabbed section becomes a sequence of bold tab labels, each followed by that tab's content. - Tags inside fenced code blocks or inline code spans are never touched by any cleaning step, including this one. ### Choose cleaning options by content type Which options to enable depends on how the docs were written: - Hand-written guides: leave both options off, or set `excludeImports: true` on MDX-heavy sites. `removeDuplicateHeadings` can drop a line you wrote on purpose. - API reference and other generated content: set both options to `true` to remove imports and repeated heading text. The configuration for generated content: ```js { excludeImports: true, removeDuplicateHeadings: true, } ``` For more combinations, see [best practices](./best-practices.md). ### Image URL rewriting (`rewriteImageUrls`) `rewriteImageUrls` is a `boolean` (default `false`). When `true`, the plugin rewrites relative image references to absolute URLs of the images in the build output. Docusaurus source files reference images with paths relative to the source file: ```md ![Architecture diagram](./img/arch.png) ![Deployment flow](../img/deploy.png) ``` During a build, Docusaurus copies these images to `build/assets/images/` with a content hash appended to the filename (for example, `arch-1a2b3c4d5e6f7890.png`). The generated Markdown and `llms-full.txt` keep the original relative paths, such as `./img/arch.png`, which point at the source file location and not at the built image. When `rewriteImageUrls: true`, the plugin scans `build/assets/images/` after the build and rewrites each relative image reference to the absolute hashed URL. A reference like this: ```md ![Architecture diagram](./img/arch.png) ``` becomes an absolute URL pointing at the hashed file: ```md ![Architecture diagram](https://yoursite.com/assets/images/arch-1a2b3c4d5e6f7890.png) ``` To enable it, typically alongside `generateMarkdownFiles`: ```js module.exports = { plugins: [ [ 'docusaurus-plugin-llms', { generateMarkdownFiles: true, rewriteImageUrls: true, }, ], ], }; ``` #### How rewriting works The plugin resolves each reference by basename: 1. After the build, it scans `build/assets/images/` and builds a lookup map from each original basename to its list of hashed build paths. 2. For every relative image reference in the generated content, it extracts the basename and looks it up in the map. 3. On a single match, it rewrites the path directly. 4. On multiple matches (two images share a filename), it compares the source image's bytes against each candidate and uses the one that matches. 5. On no match (a placeholder image, or a file in an unprocessed section), it keeps the original relative path. #### Limitations Because the plugin works from the build output, rewriting has these limits: - Only images that Docusaurus bundled into `build/assets/images/` are rewritten. An image that no rendered page references isn't in the build, so its path stays relative. - Rewriting applies to Markdown image syntax (`![alt](path)`). Cleaning strips HTML `` tags before rewriting runs. - Rewriting applies to individual `.md` files (when `generateMarkdownFiles: true`), `llms-full.txt`, and full-content custom LLM files. - With `useRelativeUrls: true`, rewritten image URLs stay absolute. ### Related pages - [Generating individual Markdown files](./markdown-files.md) - [Best practices](./best-practices.md) - [Configuration options](./configuration.md#content-cleaning) --- ## Multi-version output The `versions` option publishes a separate set of LLM files for each documentation version your site serves. The plugin runs once per version and writes each version's `llms.txt` (and any custom LLM files) under its own subdirectory, with links scoped to that version's routes. Without `versions`, the plugin writes one set of files at the site root. ### When to use it Use `versions` when one site serves several doc versions from different route prefixes. A common setup keeps a `nightly` build at the site root and a `stable` build under `/stable`. Each version gets its own `llms.txt` at `/llms.txt` and `/stable/llms.txt`, and the links inside each file resolve to that version's URLs. The root `llms.txt` links only to root pages, and `/stable/llms.txt` links only to `/stable/` pages. The top-level `version` option is a separate feature: it writes a `Version:` label into the generated files and produces one set of files. See [custom LLM files](./content-generation.md#generate-custom-llm-files) for the `version` label and per-file overrides. ### The `versions` option The `versions` option accepts either an explicit array of version objects or the string `'auto'`. Its type is `VersionConfig[] | 'auto'`, and it defaults to `undefined`. Each version object accepts these fields: | Field | Type | Default | Description | | ---------------- | ------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------- | | `name` | `string` (required) | none | Version identifier (for example `'nightly'`, `'stable'`, or `'0.0.1'`). Must be unique across the array. | | `label` | `string` | `name` | Human-readable label written into the `Version:` line of the generated files. | | `docsDir` | `string \| DocsSection[]` | top-level `docsDir` | Source docs directory (or sections) for this version, relative to the site directory. | | `path` | `string` | `name` | Output subdirectory and route prefix. Use `''` for the site root. | | `routePrefix` | `string` | `path` | Route prefix the version's links resolve under. Use `''` when the sections' `routeBasePath` holds it. | | `customLLMFiles` | `CustomLLMFile[]` | top-level `customLLMFiles` | Per-version custom LLM files. | | `includeOrder` | `string[]` | top-level `includeOrder` | Per-version include order. | Any field left unset on a version falls back to the matching top-level plugin option, so you declare shared settings like `customLLMFiles` and `includeOrder` once and override them per version. The `path` field sets both the output subdirectory under the build directory and the route prefix that the version's links must resolve to. A version with `path: 'stable'` writes its files to `/stable/` and its links resolve to `/stable/...` URLs. A version with `path: ''` writes to the site root. Set `routePrefix` when the route prefix differs from the output subdirectory; see [versions inside the route base path](#versions-inside-the-route-base-path). Each version's `docsDir`, `customLLMFiles`, and `includeOrder` get the same checks as the top-level options, and an invalid value throws a configuration error when the plugin loads. If one version fails while its files are generated, the plugin logs an error naming that version and generates the remaining versions. With `includeBlog: true`, blog posts appear in the files of the version written at the site root, or of the first version when no version writes to the root. ### Explicit versions List each version with its source directory and output `path`. This config generates `/llms.txt` from the current docs and `/stable/llms.txt` from a versioned snapshot: ```js plugins: [ [ 'docusaurus-plugin-llms', { // Shared defaults inherited by every version: customLLMFiles: [/* llms-python.txt, ... */], versions: [ { name: 'nightly', label: 'Nightly', docsDir: 'docs', path: '' }, { name: 'stable', label: 'v2.0', docsDir: 'versioned_docs/version-2.0', path: 'stable' }, ], }, ], ]; ``` Each version writes `llms.txt` (and any `customLLMFiles`) under `/`, so this example produces `/llms.txt` and `/stable/llms.txt`. The `stable` files link to `/stable/...` URLs, and the root version's links stay at the root. The array must contain at least one version, every `name` must be unique, and no two versions may resolve to the same `path`. The plugin throws a configuration error at build time if any of these constraints is violated. #### Versions inside the route base path Docusaurus serves a docs version at `///`, so a default site serves its last version at `/docs/...` and the current docs at `/docs/next/...`. To describe that layout explicitly, give each version a section whose `routeBasePath` includes the version segment, and set `routePrefix: ''`: ```js versions: [ { name: '1.0', docsDir: [{ path: 'versioned_docs/version-1.0', routeBasePath: 'docs' }], path: '', }, { name: 'current', docsDir: [{ path: 'docs', routeBasePath: 'docs/next' }], path: 'next', routePrefix: '', }, ], ``` This writes `/llms.txt` with links to `/docs/...` pages and `/next/llms.txt` with links to `/docs/next/...` pages. [Automatic detection](#automatic-detection) produces the same layout from the Docusaurus config. ### Automatic detection Set `versions: 'auto'` to derive the version list automatically from Docusaurus docs versioning: ```js plugins: [ [ 'docusaurus-plugin-llms', { customLLMFiles: [/* ... */], versions: 'auto', }, ], ]; ``` Auto mode reads the docs plugin options (from the preset's `docs` options or a `@docusaurus/plugin-content-docs` entry in `plugins`) and builds the versions the way Docusaurus does: - The versions are the current docs plus every entry in `versions.json`. The current docs are named `current` and read from the docs plugin's `path` (or your top-level `docsDir`); a versioned entry `` reads from `versioned_docs/version-/`. - `includeCurrentVersion: false` leaves out the current docs, `onlyIncludeVersions` keeps only the listed versions, and `disableVersioning: true` keeps only the current docs. - The last version is `lastVersion` when set, otherwise the first included entry in `versions.json`, otherwise `current`. - Each version's path is its `versions..path` when set. Otherwise the last version's path is `''`, the current docs' path is `'next'`, and any other version's path is its name. A version with path `` links to pages under `///` and writes its files to `//`. The last version has an empty path by default, so its files are the root `llms.txt` and `llms-full.txt`. On a default site with `versions.json` set to `["1.0"]`, the build directory looks like this: ``` build/ ├── llms.txt # 1.0, links to /docs/... ├── llms-full.txt └── next/ ├── llms.txt # current, links to /docs/next/... └── llms-full.txt ``` Each version's label follows Docusaurus: its `versions..label` from the docs plugin config when set, otherwise `Next` for the current docs and the version name for any other version. If `versions.json` is absent, the plugin generates only the current docs. ### Output layout and version identity Every version writes its files under `//`. With the [explicit example](#explicit-versions), the build directory looks like this: ``` build/ ├── llms.txt # nightly (root) links file ├── llms-full.txt # nightly full-content file └── stable/ ├── llms.txt # stable links file └── llms-full.txt # stable full-content file ``` Each generated file names its version: the plugin writes a `Version:` line with the version's `label` (or `name`) under the file's description. The `Version:` line and the per-version subdirectory identify which version a file belongs to. ### Add versions to an existing site To keep your current root output and add a version alongside it, declare the current docs as a version with `path: ''`, then add the other versions: ```js versions: [ { name: 'current', docsDir: 'docs', path: '' }, { name: '2.0', docsDir: 'versioned_docs/version-2.0', path: '2.0' }, ], ``` Every version inherits the top-level options it doesn't override. For those options, see [configuration options](./configuration.md). --- ## Ordering and path transformation These options set the order documents appear in, write links as site-relative paths, rewrite the URLs of files that match no Docusaurus route, and set the batch size for progress logging. This page also covers how the plugin resolves each page's URL. All of them are optional. For the full option list, see [configuration options](./configuration.md). For per-file variants of the ordering options, see [custom LLM files](./content-generation.md#generate-custom-llm-files). ### Document ordering By default the plugin writes documents in the order it finds them. To set the order, set `includeOrder` (type `string[]`, default `[]`) to an array of glob patterns. The plugin walks the patterns in order, and for each pattern it appends every matching file that hasn't already been placed. A file is emitted once, under the first pattern it matches, so earlier patterns win. This example groups documents into sections that appear in the listed order: ```js { includeOrder: ['getting-started/*', 'guides/*', 'api/*', 'advanced/*'], } ``` Because each file is claimed by the first pattern it matches, you can list specific files before a wildcard to pin them to the top of their group: ```js { includeOrder: [ 'getting-started/installation.md', // this file first 'getting-started/quick-start.md', // then this one 'getting-started/*.md', // then the rest of getting-started 'api/core/*.md', 'api/**/*.md', // all remaining API docs ], } ``` #### Patterns match site-relative and docs-relative paths Patterns in `includeOrder`, `ignoreFiles`, and a custom file's `includePatterns` are each tested against two forms of the file path: - The site-relative path, relative to your site root, such as `docs/quickstart/file.md`. - The docs-relative path, relative to your `docsDir`, such as `quickstart/file.md`. A pattern matches if it matches either form, so both `docs/quickstart/*` and `quickstart/*` select the same files. The docs-relative form keeps working if you rename or move `docsDir`, so prefer it. #### Handling files that don't match `includeUnmatchedLast` (type `boolean`, default `true`) controls what happens to files that no `includeOrder` pattern matched. When it's `true`, those files are appended after the ordered ones, so nothing is dropped. When it's `false`, the plugin includes only files that match an `includeOrder` pattern. This configuration includes only the files under `public-docs/` and drops everything else: ```js { includeOrder: ['public-docs/**/*.md'], includeUnmatchedLast: false, } ``` Nesting depth follows the glob you write. `tutorials/beginner/**/*` matches beginner tutorials at any depth, while `tutorials/intermediate/*` matches only the immediate children of that directory: ```js { includeOrder: [ 'tutorials/beginner/**/*', // all beginner tutorials, any depth 'tutorials/intermediate/*', // intermediate tutorials, one level 'tutorials/**/*', // everything else under tutorials ], } ``` Custom LLM files have their own `orderPatterns` and `includeUnmatchedLast` fields. See [custom LLM files](./content-generation.md#generate-custom-llm-files) for details. ### How page URLs are resolved The plugin links each page to the route Docusaurus built for it, which it receives through the `postBuild` hook. It derives the route from the page's file path and front matter the way Docusaurus does: - Numeric ordering prefixes such as `01-` are dropped from every folder and file name, so `docs/01-guides/02-setup.md` links to `/docs/guides/setup`. - `index.md`, `README.md`, and a file named after its folder (`api/api.md`), in any letter case, link to their folder's URL. - A front matter `slug` that starts with `/` replaces the page's path under the section's `routeBasePath`: `slug: /start` in `docs/guides/intro.md` links to `/docs/start`. - Any other `slug` replaces the file name and keeps the folder: `slug: setup` in `docs/01-guides/intro.md` links to `/docs/guides/setup`. - A front matter `id` replaces the file name the same way, except on index, `README`, and folder-named files, where Docusaurus ignores it. - A page matches routes under its own section's `routeBasePath` first, and docs pages never link to blog routes. When `generateMarkdownFiles` is on, each `.md` file's path follows the same route, so `slug: /start` writes `docs/start.md`. ### Relative links `useRelativeUrls` (type `boolean`, default `false`) writes each link in `llms.txt` and in links-only custom files as a path from the site root, keeping the `baseUrl`: ``` - [Intro](/sub/docs/intro): Docs intro. ``` Use it when the same build is served from more than one host, or when the site `url` in `docusaurus.config.js` isn't the URL readers use. Image URLs written by `rewriteImageUrls` stay absolute. ### Path transformation The plugin resolves each document's URL by matching the file path against the routes Docusaurus built, which it receives through the `postBuild` hook. `pathTransformation` applies only to files that match no known route, so most sites don't need it. `pathTransformation` (type `object`, default `undefined`) takes two arrays, each defaulting to `[]`: - `pathTransformation.ignorePaths` (type `string[]`): path segments to remove from the URL when they're present, including the section's route base path. - `pathTransformation.addPaths` (type `string[]`): path segments to add after the route base path when they aren't already there. The examples below use `docsDir: 'docs'`. To strip a leading `docs` segment so it doesn't appear in the URL, use `ignorePaths`: ```js pathTransformation: { ignorePaths: ['docs'], } ``` With that setting, the file `docs/manual/decorators.md` resolves to `https://example.com/manual/decorators`. To add a segment such as `api`, use `addPaths`: ```js pathTransformation: { addPaths: ['api'], } ``` That turns `docs/manual/decorators.md` into `https://example.com/docs/api/manual/decorators`. The route base path `docs` stays in front. To replace the route base path, combine both. The plugin removes the ignored segments first, then adds the new ones: ```js pathTransformation: { ignorePaths: ['docs'], addPaths: ['api'], } ``` That maps `docs/manual/decorators.md` to `https://example.com/api/manual/decorators`. Both arrays accept multiple segments. ### Batch processing for large sites `processingBatchSize` (type `number`, default `100`) sets how many documents the plugin assembles into `llms-full.txt` per batch. Batches run one after another, and document order is preserved across batch boundaries. The plugin reads and processes every document before batching starts, so the batch size doesn't change peak memory use or the output. It changes how often verbose mode logs progress: with more than one batch, the plugin logs a line per batch. Set it to a positive integer. This configuration logs progress every 50 documents: ```js module.exports = { plugins: [ [ 'docusaurus-plugin-llms', { logLevel: 'verbose', processingBatchSize: 50, }, ], ], }; ``` For more on verbose mode, see [logging](./configuration.md#logging). --- ## Best practices This page lists option settings for API reference, tutorials, and multi-language docs, plus notes on deployment, build time, and memory. For each option's full reference, see [configuration options](./configuration.md) and [content cleaning](./content-cleaning.md). ### API documentation Auto-generated API docs (OpenAPI output, for example) carry React component imports and short sections that repeat their own heading. Turn on both content-cleaning options and generate a single full-content file so an LLM can read the whole reference at once: ```js { excludeImports: true, // Remove React component imports removeDuplicateHeadings: true, // Remove lines that repeat an endpoint heading generateLLMsFullTxt: true, // Write every page to one file } ``` Both `excludeImports` and `removeDuplicateHeadings` are `boolean` and default to `false`, so you have to opt in. ### Tutorial content For hand-written tutorials and guides, clean up MDX imports but leave the prose alone, since duplicate-heading removal can strip content you wrote on purpose. Use `includeOrder` to set the reading order: ```js { excludeImports: true, // Remove any MDX imports removeDuplicateHeadings: false, // Keep all content as written includeOrder: [ // Reading order 'getting-started/*', 'tutorials/*', 'advanced/*', ], } ``` `includeOrder` is a `string[]` of glob patterns and defaults to `[]`. Files that don't match any pattern are appended at the end, because `includeUnmatchedLast` defaults to `true`. Set it to `false` if you want `includeOrder` to act as a strict allowlist. ### Multi-language documentation If your docs cover more than one programming language, generate a separate file per language with `customLLMFiles`. Each entry needs a `filename`, an `includePatterns` array, and a `fullContent` flag: ```js { excludeImports: true, removeDuplicateHeadings: true, customLLMFiles: [ { filename: 'llms-python.txt', includePatterns: ['**/python/**/*.md'], fullContent: true, title: 'Python Documentation', }, { filename: 'llms-javascript.txt', includePatterns: ['**/javascript/**/*.md'], fullContent: true, title: 'JavaScript Documentation', }, ], } ``` `customLLMFiles` is an array and defaults to `[]`. See [custom LLM files](./content-generation.md) for the full field reference, including `ignorePatterns`, `orderPatterns`, and per-file `version`. ### Validating the deployed output The plugin builds `llms.txt` from your site's build routes and can't see the deployed site. Host redirect rules, `trailingSlash` settings, and docs restructures can break the served `llms.txt` after a successful build, leaving an empty file or dead links. [llms-txt-check](https://github.com/portdeveloper/llms-txt-check) checks the deployed file against what your site serves: ```yaml - run: npx llms-txt-check https://your-docs-site.com ``` It exits nonzero when the file or any listed URL stops serving, so the deploy step fails when a route breaks. Run it as a final step in your deploy pipeline, after the site is live. ### Build time and memory The plugin does all its work in the `postBuild` hook, after Docusaurus has built the site, and the cleaning options add little time to that step. The plugin holds every processed document in memory while it writes `llms-full.txt`, so memory use grows with the size of your docs. On a memory-constrained runner, give Node.js more heap with `NODE_OPTIONS=--max-old-space-size=4096`.