---
title: Remark LLMs
description: Generate markdown for LLM consumption with customizable placeholders
---

The `remarkLLMs` plugin stringifies the processed Markdown AST to plain Markdown, the output is generated as an ESM export. This is useful for feeding content to LLMs, search indexing, or other text-based processing.

## Usage

### Fumadocs MDX

Enable `includeProcessedMarkdown` in your collection config, Fumadocs MDX runs `remarkLLMs` internally when this is set.

```ts title="source.config.ts"
import { defineDocs } from 'fumadocs-mdx/config';
import { type LLMsOptions } from 'fumadocs-core/mdx-plugins/remark-llms';

const llmsOptions: LLMsOptions = {
  // options...
};

export default defineDocs({
  docs: {
    postprocess: {
      includeProcessedMarkdown: llmsOptions,
    },
  },
});
```

The stringified Markdown is exported as `_markdown` and available via `getText('processed')`, see [Collections](/docs/mdx/collections#includeprocessedmarkdown) for details.

### MDX Compiler

When using the MDX Compiler (or a custom pipeline), add the plugin:

```ts
import { compile } from '@mdx-js/mdx';
import { remarkLLMs, type LLMsOptions } from 'fumadocs-core/mdx-plugins/remark-llms';

const llmsOptions: LLMsOptions = {
  // The output will be exported as `_markdown`
  as: '_markdown',
};

const vfile = await compile('...', {
  remarkPlugins: [[remarkLLMs, llmsOptions]],
});
```

## Placeholders

Placeholders let you keep certain MDX components in the stringified output as special tokens instead of dropping or inlining them. That way you can:

1. Rehydrate those tokens later via `renderPlaceholder()`.
2. Generate a better Markdown representation with runtime data.

### Using `mdxAsPlaceholder`

List component names to treat as placeholders:

```ts
import { type LLMsOptions } from 'fumadocs-core/mdx-plugins/remark-llms';

const llmsOptions: LLMsOptions = {
  // [!code ++]
  mdxAsPlaceholder: ['Callout', 'Card'],
};
```

In the exported Markdown, `<Callout>` and `<Card>` will appear as placeholder tokens, like:

```
\0{"name": "Callout", ...}\0
```

### Using `stringify`

For full control, use the `stringify` option and with the `placeholder()` helper:

```ts
import { placeholder, type LLMsOptions } from 'fumadocs-core/mdx-plugins/remark-llms';

const llmsOptions: LLMsOptions = {
  stringify(node, parent, state, info) {
    if (node.type === 'mdxJsxFlowElement' && node.name === 'MyPage') {
      return placeholder(node, parent, state, info);
    }
  },
};
```

### Rendering Placeholders

Use `renderPlaceholder()` from the runtime module to replace placeholder tokens with custom output:

```ts
import { renderPlaceholder } from 'fumadocs-core/mdx-plugins/remark-llms.runtime';

const markdown = await getExportedMarkdown(); // e.g. from _markdown

const rendered = await renderPlaceholder(markdown, {
  async Callout({ name, attributes, children }) {
    // note: you can also fetch data here
    const data = await fetchData();
    return `[${attributes.type ?? 'info'}] ${children}`;
  },
  Card({ attributes, children }) {
    return `**${attributes.title}**\n\n${children}`;
  },
});
```

Each renderer receives `PlaceholderData`:

- `name`
- `attributes`
- `children` (the stringified child content).

## Example

```mdx tab="Input"
# Getting started

<Callout type="tip">Hello **world**.</Callout>

Some paragraph.
```

```md tab="Output"
# Getting started [#getting-started]

\0{"name":"Callout","attributes":{"type":"tip"},"children":"Hello **world**."}\0

Some paragraph.
```

## Options

<auto-type-table
  type="import('fumadocs-core/mdx-plugins/remark-llms').LLMsOptions"
/>
