> For AI agents: the complete documentation index is available at https://rspress.rs/llms.txt, the full documentation bundle is available at https://rspress.rs/llms-full.txt.

# llms.txt (SSG-MD)

> Want to get started quickly? Jump to [Quick Start](#quick-start).

## What is SSG-MD?

Rspress provides the experimental Static Site Generation to Markdown (SSG-MD) feature. Similar to [Static Site Generation (SSG)](https://rspress.rs/guide/basic/ssg.md), SSG-MD renders pages as Markdown files instead of HTML files and generates [`llms.txt`](#llms-txt) and `llms-full.txt`, making technical documentation easier for large language models to understand and use.

The following table compares SSG and SSG-MD:

| Comparison              | SSG                                        | SSG-MD                               |
| ----------------------- | ------------------------------------------ | ------------------------------------ |
| **Full Name**           | Static Site Generation                     | Static Site Generation to Markdown   |
| **Optimization Target** | SEO (Search Engine Optimization)           | GEO (Generative Engine Optimization) |
| **Target Consumer**     | Search engine crawler                      | LLM / vector retrieval system        |
| **Index File**          | [`sitemap.xml`](https://www.sitemaps.org/) | [`llms.txt`](https://llmstxt.org/)   |
| **Full Content File**   | -                                          | `llms-full.txt`                      |
| **Core Implementation** | `renderToString`                           | `renderToMarkdownString`             |
| **Access Method**       | `/guide/start/introduction.html`           | `/guide/start/introduction.md`       |

## What is llms.txt? \{#llms-txt}

[`llms.txt`](https://llmstxt.org/) is an emerging standard file format placed in a website's root directory to help large language models better understand and use website content.

Because LLMs have limited context windows, they usually cannot process an entire website's HTML content. Converting complex HTML with navigation, ads, and JavaScript to plain text is also difficult and imprecise. `llms.txt` solves this by providing a structured Markdown index that includes page URLs and content descriptions, helping AI tools quickly locate and understand key information.

In simple terms:

- `sitemap.xml` → "Site map" for search engines
- `llms.txt` → "Documentation index" for AI

Output structure example:

```tree
doc_build
├── llms.txt
├── llms-full.txt
├── guide
│   └── start
│       └── introduction.md
└── ...
```

`llms.txt` content example:

```txt
# Rspress

> Rspress is a static site generator based on Rspack.

## Docs

- [Introduction](/guide/start/introduction.md): Introduction to Rspress
- [Quick Start](/guide/start/quick-start.md): Quick Start
```

## Why SSG-MD?

In React-based frontend frameworks, extracting static information from dynamically rendered content is often difficult. MDX has the same challenge: `.mdx` files contain Markdown content but can also embed React components, making docs more interactive. Rspress lets users enhance docs with MDX fragments, React components, Hooks, and TSX routes, but this dynamic content creates problems when converted to Markdown:

- Feeding raw MDX to AI introduces code syntax noise and loses rendered React component content.

- Converting HTML to Markdown often produces poor results, making information quality hard to guarantee.

[Static Site Generation (SSG)](https://rspress.rs/guide/basic/ssg.md) generates static HTML for crawlers and improves [SEO](https://en.wikipedia.org/wiki/Search_engine_optimization). SSG-MD addresses a similar problem for AI tools: it improves [GEO](https://en.wikipedia.org/wiki/Generative_engine_optimization) and the quality of static information for large language models. Compared with HTML-to-Markdown conversion, rendering from React's virtual DOM gives SSG-MD a richer information source.


![SSG-MD rendering flow](https://assets.rspack.rs/rspress/assets/ssg-md-flow.jpg)

## How does SSG-MD work?

1. Rspress internally implements a `renderToMarkdownString` method similar to `renderToString` in `react-dom`, which renders React components to Markdown strings:

```tsx
import { renderToMarkdownString } from 'react-render-to-markdown';

// HTML elements are converted to the corresponding Markdown syntax
renderToMarkdownString(
  <div>
    <strong>foo</strong>
    <span>bar</span>
  </div>,
);
// Output: '**foo**bar'

// React components and Hooks are supported
const Article = () => {
  return (
    <>
      <h1>Hello World</h1>
      <p>This is a paragraph.</p>
    </>
  );
};
renderToMarkdownString(<Article />);
// Output: '# Hello World\n\nThis is a paragraph.\n'
```

In principle, this API works for any site built with React; see [react-render-to-markdown](https://www.npmjs.com/package/react-render-to-markdown) if you're interested.

2. Rspress uses a custom remark plugin `remarkSplitMdx` to preprocess MDX files before rendering. This plugin splits the MDX AST, separating pure Markdown content from JSX components: Markdown text is serialized as string literals, while JSX components and MDX expressions (e.g., `{variable}`) are preserved as React elements. This ensures that Markdown content passes through as-is without being processed by React rendering, while dynamic components are rendered by `renderToMarkdownString`.

For example, the following MDX:

```mdx
# Hello

Some **bold** text.

<PackageManagerTabs command="install rspress" />

{window.title}
```

It is transformed into a component like this:

```tsx
function _createMdxContent() {
  return (
    <>
      {'# Hello\n\nSome **bold** text.\n'}
      <PackageManagerTabs command="install rspress" />
      {window.title}
    </>
  );
}
```

3. Rspress provides the `import.meta.env.SSG_MD` environment variable so React components can distinguish SSG-MD rendering from browser rendering and customize their output:

```tsx
export function Tab({ label }: { label: string }) {
  if (import.meta.env.SSG_MD) {
    return <>{`** Here is a Tab named ${label}**`}</>;
  }
  return <div>{label}</div>;
}
```

4. Rspress's internal component library is adapted for SSG-MD, so components render meaningful Markdown during the SSG-MD phase. For example:

```tsx
<PackageManagerTabs command="create rspress@latest" />
```

It is rendered as:

````md
```sh [npm]
npm create rspress@latest
```

```sh [yarn]
yarn create rspress
```

```sh [pnpm]
pnpm create rspress@latest
```

```sh [bun]
bun create rspress@latest
```

```sh [deno]
deno init --npm rspress@latest
```
````

## Quick start

Enable `llms` in `rspress.config.ts`:

```ts title="rspress.config.ts"
import { defineConfig } from '@rspress/core';

export default defineConfig({
  llms: true,
});
```

After running `rspress build`, the output directory (default `doc_build`) will additionally contain the following files:

```tree
doc_build
├── llms.txt          # Index file with titles and descriptions in navigation order
├── llms-full.txt     # Contains Markdown content of all pages
├── guide
│   └── start
│       └── introduction.md   # Corresponding .md file for each page
└── ...
```

Access pages by replacing the `.html` suffix with `.md`, e.g., `/guide/start/introduction.md`. Multilingual sites will output `{lang}/llms.txt` and `{lang}/llms-full.txt` for non-default languages.

:::warning

`llms` is experimental and may have stability or compatibility issues. If SSG-MD cannot be enabled because of SSR incompatibility, use [@rspress/plugin-llms](https://rspress.rs/plugin/official-plugins/llms.md).

:::

:::info React 18 Support

SSG-MD uses `react-render-to-markdown@19` by default, which only supports React 19. If you use React 18, install `react-render-to-markdown@18` in your `package.json`:

```json title="package.json"
{
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-render-to-markdown": "^18.3.1"
  }
}
```

After installation, Rspress automatically detects and uses the `react-render-to-markdown@18` version from your project.

:::

## Configuration

### UI display

When `llms: true` is enabled, `LlmsCopyButton` and `LlmsViewOptions` components are automatically displayed below all H1 headers, allowing users to copy Markdown content or open it in AI tools like ChatGPT or Claude. You can also display them in the outline panel instead by setting `placement: 'outline'`.

Customize or disable via `themeConfig.llmsUI`:

```ts title="rspress.config.ts"
import { defineConfig } from '@rspress/core';

export default defineConfig({
  llms: true,
  themeConfig: {
    // Disable LLMS UI
    llmsUI: false,
    // Or customize options:
    // llmsUI: {
    //   injectLlmsHint: false, // Disable the HTML/Markdown directive hint for LLMs
    //   viewOptions: ['markdownLink', 'chatgpt', 'claude'],
    //   placement: 'outline', // Display in outline panel instead of below H1
    // },
  },
});
```

For more information, see [themeConfig.llmsUI](https://rspress.rs/api/config/config-theme.md#llmsui).

### Customize llms.txt

Use `llms.llmsTxt` to compose the complete contents of each generated `llms.txt` file:

```ts title="rspress.config.ts"
import { defineConfig } from '@rspress/core';

export default defineConfig({
  llms: {
    llmsTxt: ({ title, description, sections }) => {
      const sectionContent = sections
        .map(section => {
          const pages = section.pages
            .map(page => `- [${page.title}](${page.link})`)
            .join('\n');
          return `## ${section.title}\n\n${pages}`;
        })
        .join('\n\n');

      return `# ${title}

> ${description}

${sectionContent}`;
    },
  },
});
```

The callback can return a string or a Promise. It runs once for every generated language and version combination, and receives:

- `title` and `description`: The site metadata from `rspress.config.ts`.
- `lang` and `version`: The language and version of the current `llms.txt` file.
- `base` and `siteOrigin`: The URL configuration used to generate Markdown links.
- `sections`: Pages grouped by navigation and ordered by the sidebar. Each page contains `title`, `description`, `frontmatter`, `routePath`, `link`, `lang`, and `version`. The `link` field is the final URL of the generated Markdown file.

Pages that do not match a navigation item are placed in an `Others` section. The locale home page is omitted because the site title and description represent it.

### Custom MDX splitting

When documents contain custom components, use `remarkSplitMdxOptions` to control which components to keep or convert to plain text when converting to Markdown:

```ts title="rspress.config.ts"
import { defineConfig } from '@rspress/core';

export default defineConfig({
  llms: {
    remarkSplitMdxOptions: {
      excludes: [[['Demo'], '@project/components']],
    },
  },
});
```

- `excludes`: Matched components are converted to plain text and have the highest priority.
- `includes`: If set, only matched components are retained; all others are converted to plain text.
- When both are configured, `excludes` is applied first, then the result is filtered by `includes`.
