> 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.

# @rspress/plugin-rss 

[Source Code](https://github.com/web-infra-dev/rspress/tree/main/packages/plugin-rss)

Generates RSS files for selected documentation pages with [feed](https://github.com/jpmonette/feed).

## Installation


```sh [npm]
npm add @rspress/plugin-rss -D
```

```sh [yarn]
yarn add @rspress/plugin-rss -D
```

```sh [pnpm]
pnpm add @rspress/plugin-rss -D
```

```sh [bun]
bun add @rspress/plugin-rss -D
```

```sh [deno]
deno add npm:@rspress/plugin-rss -D
```

### Update Rspress config

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

export default defineConfig({
  plugins: [
    pluginRss({
      // The URL of your documentation site
      siteUrl: 'https://example.com',
      // ...more configurations below
    }),
  ],
});
```

By default, this plugin generates a `blog.xml` file in the `doc_build/rss/` folder for all pages starting with `/blog/`.

The RSS file can be accessed via `/rss/blog.xml`.

:::tip
This plugin only works with `rspress build` and does not generate RSS files on `rspress dev`.
:::

## Usage

### Selecting pages to be included in RSS

Use the `feed.test` option to select which pages are included in the RSS file.

```ts
pluginRss({
  // ...
  feed: { test: '/zh/blog' },
});
```

### Requirements

All documents included in the RSS must have either `date` or `published_at` in frontmatter to keep RSS updates stable for readers.

```md
---
published_at: 2024-01-10 08:00:00
---

Or frontmatter `date`.
```

### Generating multiple RSS files

Sometimes you may need to generate multiple RSS files, for example for different languages or categories.

Pass a list of RSS options to `feed`:

```ts
pluginRss({
  feed: [
    { id: 'blog', test: '/blog/', title: 'Rspress Blog', language: 'en-US' },
    {
      id: 'blog-zh',
      test: '/zh/blog/',
      title: 'Rspress 博客',
      language: 'zh-CN',
    },
    {
      id: 'rspack',
      test: ({ frontmatter }) => frontmatter.categories.includes('rspack'),
      title: 'Rspack Releases',
      language: 'en-US',
    },
    {
      id: 'rsbuild',
      test: ({ frontmatter }) => frontmatter.categories.includes('rsbuild'),
      title: 'Rsbuild Releases',
      language: 'en-US',
    },
  ],
});
```

The options above will generate four RSS files: `blog.xml`, `blog-zh.xml`, `rspack.xml`, `rsbuild.xml`, all located in the `rss` folder.

### Modifying the output path

You can customize the output path using the `output` and `feed.output` parameters.

See [FeedOutputOptions](#feedoutputoptions) below.

### Linking RSS to doc pages

By default, this plugin inserts a `<link rel="alternate">` tag into selected pages included in the RSS. The tag points to the RSS file URL, so RSS readers can detect it automatically.

To insert this tag into pages that are not included in the RSS, such as the homepage, add `link-rss` frontmatter with the feed ID as the value. For example:

```markdown
---
link-rss: blog
---

This frontmatter inserts a `<link rel="alternate">` tag into this page and points it to the RSS URL of the `blog` feed.

However, this page itself will not be included in that RSS.
```

`link-rss` also supports inserting multiple `<link>` tags associated with feed ids on a single page:

```markdown
---
link-rss:
  - blog
  - releases
---
```

### Customize RSS content

The RSS file consists of two parts: the RSS basic information, known as the `channel` in the RSS format, and the list of articles, known as the `item` in the RSS format.

Customize each part as follows:

- The `channel` can be fully modified through the `feed` option. See [Other Options](#other-options) below.
- The `item` can be fully modified through the `feed.item` option. See the [item](#item) section below.

## Options

### PluginRssOptions

Plugin options.

```ts
export interface PluginRssOptions {
  siteUrl?: string;
  feed?: Partial<FeedChannel> | FeedChannel[];
  output?: Omit<FeedOutputOptions, 'filename'>;
}
```

#### `siteUrl`

- **Type**: `string`
- **Default**: [`siteOrigin`](https://rspress.rs/api/config/config-basic.md#siteorigin) + [`base`](https://rspress.rs/api/config/config-basic.md#base), or `base` when `siteOrigin` is not configured

The site URL of the current documentation site. It is used in the RSS file.

RSS links are consumed outside the documentation page context, so configure an absolute URL with protocol and domain, such as `https://example.com/base/`. When `base` is configured, plugin-level `siteUrl` must include the `base` path.

If [`siteOrigin`](https://rspress.rs/api/config/config-basic.md#siteorigin) and [`base`](https://rspress.rs/api/config/config-basic.md#base) are configured in Rspress, you can omit the plugin-level `siteUrl`. The full URL concatenation order is `siteOrigin + base + routePath`. If neither plugin-level `siteUrl` nor `siteOrigin` is configured, the plugin falls back to `base`, which keeps existing relative path behavior but does not generate absolute RSS links.

```ts
// rspress.config.ts
import path from 'path';
import { defineConfig } from '@rspress/core';
import { pluginRss } from '@rspress/plugin-rss';

export default defineConfig({
  siteOrigin: 'https://example.com',
  base: '/base/',
  plugins: [
    // siteUrl defaults to 'https://example.com/base/'
    pluginRss(),
  ],
});
```

#### `feed`

- **Type**: `FeedChannel | FeedChannel[]`
- **Default**: `{ id: 'blog', test: '/blog/' }`

RSS configuration. Pass an array to generate multiple RSS files.

See [FeedChannel](#feedchannel) for more information.

#### `output`

- **Type**: `Omit<FeedOutputOptions, "filename">`
- **Default**: `{ dir: 'rss', type: 'atom' }`

Output options. See [FeedOutputOptions](#feedoutputoptions) below.

### FeedChannel

RSS file options.

```ts
export interface FeedChannel extends Partial<FeedOptions> {
  id: string;
  test:
    RegExp | string | (RegExp | string)[] | ((item: PageIndexInfo) => boolean);
  item?: (
    item: FeedItem,
    page: PageIndexInfo,
    siteUrl: string,
  ) => FeedItem | PromiseLike<FeedItem>;
  output?: FeedOutputOptions;
}
```

#### `id`

- **Type**: `string`
- **Required**

The RSS feed ID, which must be unique across multiple RSS options. It is also the default file basename for the RSS file.

#### `test`

- **Type**: `RegExp | string | (RegExp | string)[] | ((item: PageIndexInfo) => boolean)`
- **Required**

Selects documents to include in the RSS. Supported values:

- `RegExp`: Regular expression that matches the document route.
- `string`: Prefix-based match against the document route.
- `(item: PageIndexInfo) => boolean`: Match pages based on page data and frontmatter. This is also the recommended way to include a route prefix while excluding specific pages such as `/blog/`.

:::tip
`item.routePath` does not include the `base` path.
:::

For example, if you only want to include article pages under `/blog/` but exclude the blog index page itself:

```ts
feed: {
  id: 'blog',
  test: (item) => {
    return (
      item.routePath.startsWith('/blog/') && item.routePath !== '/blog/'
    );
  },
}
```

#### `item`

- **Type**: `(item: FeedItem, page: PageIndexInfo, siteUrl: string) => FeedItem | PromiseLike<FeedItem>`
- **Default**: <SourceCode href="https://github.com/web-infra-dev/rspress/blob/main/packages/plugin-rss/src/createFeed.ts" />

Generates structured data for each article in the RSS file.

Refer to the type of structured data: 

[Source Code](https://github.com/jpmonette/feed/blob/8ca7f3e4e8e421e2a2632bb9524385e86f30744c/src/typings/index.ts#L1-L25)

The plugin has a built-in generator that uses document frontmatter and page data.

For example, RSS `content` uses `summary` from frontmatter first, then falls back to document content.

Provide the `item` function to modify the generated data passed as the first parameter.

For example, the following configuration truncates the content of articles in the RSS:

```ts
const item: FeedChannel['item'] = item => ({
  ...item,
  content: item.content.slice(0, 1000),
});
```

#### `output`

- **Type**: `FeedOutputOptions`
- **Default**: Uses the plugin's `output` option by default

Compared with the plugin-level `output` option, this option also includes `filename` for changing the output filename.

See [FeedOutputOptions](#feedoutputoptions) below.

#### Other options

`FeedChannel`
 also inherits `FeedOptions`
 from the feed package. See 

[Source Code](https://github.com/jpmonette/feed/blob/8ca7f3e4e8e421e2a2632bb9524385e86f30744c/src/typings/index.ts#L48-L67)

 for options not listed here.
### FeedOutputOptions

RSS output options. They are available both at the plugin level and the `feed` level, with the following type:

```ts
interface FeedOutputOptions {
  dir?: string;
  type?: 'atom' | 'rss' | 'json';
  filename?: string;
  publicPath?: string;
  sorting?: (left: FeedItem, right: FeedItem) => number;
  transform?: (
    content: string,
    context: {
      type: 'atom' | 'rss' | 'json';
      feed: Feed;
      channel: FeedChannel;
    },
  ) => string | PromiseLike<string>;
}
```

Example:

```ts
pluginRss({
  // Applied to all RSS outputs
  output: {
    // Change the output folder for RSS files to 'feeds', relative to `doc_build`
    dir: 'feeds',
    // Output in RSS 2.0 format, use `.rss` extension by default.
    type: 'rss',
  },
  feed: [
    {
      id: 'blog',
      test: '/blog/',
      title: 'My Blog',
      output: {
        type: 'atom' /* default to using `id` as the base file name */,
      },
    },
    {
      id: 'releases',
      test: '/releases/',
      title: 'Releases',
      output: { dir: 'releases', filename: 'feed.rss' },
    },
  ],
});
```

Building with the options above will output two files: `feeds/blog.xml` and `releases/feed.rss`.

#### `dir`

- **Type**: `string`
- **Default**: `rss`

Output folder for RSS files, relative to `doc_build`.

#### `type`

- **Type**: `"atom" | "rss" | "json"`
- **Default**: `atom`

RSS output format. The default is `atom`:

| Value  | Format                                                 | Default Extension | MIME Type              |
| ------ | ------------------------------------------------------ | ----------------- | ---------------------- |
| `atom` | [Atom 1.0](https://www.ietf.org/rfc/rfc4287.txt)       | `.xml`            | `application/atom+xml` |
| `rss`  | [RSS 2.0](https://www.rssboard.org/rss-specification)  | `.rss`            | `application/rss+xml`  |
| `json` | [JSON Feed 1.1](https://www.jsonfeed.org/version/1.1/) | `.json`           | `application/json`     |

#### `filename`

- **Type**: `string`
- **Default**: ID as the file basename; extension by RSS output format

Modifies the full filename of the RSS file.

#### `publicPath`

- **Type**: `string`
- **Default**: the value of `siteUrl`

URL prefix for the RSS file. An RSS URL is composed of `publicPath`, `dir`, and `filename`.

#### `sorting`

- **Type**：`sorting?: (left: FeedItem, right: FeedItem) => number;`

Sorts articles. By default, newest articles appear first.

#### `transform`

- **Type**: `(content: string, context: { type: 'atom' | 'rss' | 'json'; feed: Feed; channel: FeedChannel }) => string | PromiseLike<string>`

Transform the final generated feed content before it is written to disk. This hook is available in both the plugin-level `output` option and the per-feed `output` option, so the same customization pattern can be used for Atom, RSS, and JSON Feed outputs.

For example, the following configuration injects a custom `folo:id` into XML feeds and adds the same identifier to JSON Feed:

```ts
pluginRss({
  siteUrl: 'https://example.com/',
  output: {
    transform(content, { type, channel }) {
      if (type === 'json') {
        return JSON.stringify({
          ...JSON.parse(content),
          foloId: channel.id,
        });
      }

      const closingTag = type === 'rss' ? '</channel>' : '</feed>';
      return content.replace(
        closingTag,
        `<folo:id>${channel.id}</folo:id>${closingTag}`,
      );
    },
  },
});
```
