Plugin API

In the previous section, we introduced the basic structure of the plugin. In this section, we will introduce the API of the plugin to help you understand the abilities of the plugin in more detail.

TIP

In order to get better type hints, you can install @rspress/shared in the project, and then import the RspressPlugin type through import { RspressPlugin } from '@rspress/shared'.

globalStyles

  • Typestring

It is used to add a global style, and the absolute path of a style file is passed in, and the usage is as follows:

plugin.ts
import { RspressPlugin } from '@rspress/shared';
import path from 'path';

export function pluginForDoc(): RspressPlugin {
  // style path
  const stylePath = path.join(__dirname, 'some-style.css');
  return {
    // plugin name
    name: 'plugin-name',
    globalStyles: path.join(__dirname, 'global.css'),
  };
}

For example, if you want to modify the theme color, you can do so by adding a global style:

global.css
:root {
  --rp-c-brand: #ffa500;
  --rp-c-brand-dark: #ffa500;
  --rp-c-brand-darker: #c26c1d;
  --rp-c-brand-light: #f2a65a;
  --rp-c-brand-lighter: #f2a65a;
}

globalUIComponents

  • Type(string | [string, object])[]

It is used to add global components, passing in an array, each item in the array is the absolute path of a component, the usage is as follows:

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  // component path
  const componentPath = path.join(__dirname, 'foo.tsx');
  return {
    // plugin name
    name: 'plugin-comp',
    // Path to global components
    globalUIComponents: [componentPath],
  };
}

The item of globalUIComponents can be a string, which is the path of the component file, or an array, the first item is the path of the component file, and the second item is the component props, for example:

rspress.config.ts
import { defineConfig } from 'rspress/config';
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  // component path
  const componentPath = path.join(__dirname, 'foo.tsx');
  return {
    // plugin name
    name: 'plugin-comp',
    globalUIComponents: [
      [
        path.join(__dirname, 'components', 'MyComponent.tsx'),
        {
          foo: 'bar',
        },
      ],
    ],
  };
}

When you register global components, the framework will automatically render these React components in the theme without manually importing and using them.

Through global components, you can complete many custom functions, such as:

compUi.tsx
import React from 'react';

// Need a default export
// The props comes from your config
export default function PluginUI(props?: { foo: string }) {
  return <div>This is a global layout component</div>;
}

In this way, the content of the component will be rendered in the theme page, such as adding BackToTop button.

In the meanwhile, you can also use the global component to register some side effects, such as:

compSideEffect.tsx
import { useEffect } from 'react';
import { useLocation } from 'rspress/runtime';

// Need a default export
export default function PluginSideEffect() {
  const { pathname } = useLocation();
  useEffect(() => {
    // Executed when the component renders for the first time
  }, []);

  useEffect(() => {
    // Executed when the route changes
  }, [pathname]);
  return null;
}

This way, side effects of components are executed in the theme page. For example, some of the following scenarios require side effects:

  • Redirect for certain page routes.

  • Bind click event on the img tag of the page to implement the image zoom function.

  • When the route changes, the PV data of different pages are reported.

  • ......

builderConfig

  • TypeRsbuildConfig

The bottom layer of Rspress is based Rsbuild for document build process. The Rsbuild can be configured through builderConfig. For specific configuration options, please refer to [Rsbuild(https://rsbuild.dev/config/).

Of course, if you want to configure Rspack directly, you can also configure it through buildConfig.tools.rspack.

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(slug: string): RspressPlugin {
  return {
    name: 'plugin-name',
    // Global variable definitions for build phase
    builderConfig: {
      source: {
        define: {
          SLUG: JSON.stringify(slug),
        },
      },
      tools: {
        rspack(options) {
          // Modify rspack config
        },
      },
    },
  };
}

config

  • Type(config: DocConfig, utils: ConfigUtils) => DocConfig | Promise<DocConfig>

The type of ConfigUtils is as follows:

interface ConfigUtils {
  addPlugin: (plugin: RspressPlugin) => void;
  removePlugin: (pluginName: string) => void;
}

It is used to modify/extend the configuration of Rspress itself. For example, if you want to modify the title of the document, you can do it through config:

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  return {
    // Plugin name
    name: 'plugin-name',
    // Extend the config of the Rspress itself
    config(config) {
      return {
        ...config,
        title: 'New Document Title',
      };
    },
  };
}

If it involves adding and removing a plugin, you need to implement it through addPlugin and removePlugin:

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  return {
    // Plugin name
    name: 'plugin-name',
    // Extend the config of Rspress itself
    config(config, utils) {
      // Add a plugin
      utils.addPlugin({
        name: 'plugin-name',
        // ... other config of the plugin
      });
      // Remove a plugin, pass in the name of the plugin
      utils.removePlugin('plugin-name');
      return config;
    },
  };
}

beforeBuild/afterBuild

  • Type(config: DocConfig, isProd: boolean) => void | Promise<void>

It is used to perform some operations before/after the document is built. The first parameter is the config of the document, and the second parameter is whether it is currently a production environment. The usage is as follows:

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  return {
    name: 'plugin-name',
    // Hook to execute before build
    async beforeBuild(config, isProd) {
      // Do something here
    },
    // Hook to execute after build
    async afterBuild(config, isProd) {
      // Do something here
    },
  };
}
TIP

When the beforeBuild hook is executed, the config plugins of all plugins have been processed, so the config parameter already represents the final document configuration.

markdown

  • Type{ remarkPlugins?: Plugin[]; rehypePlugins?: Plugin[] }

It is used to extend the compilation ability of Markdown/MDX. If you want to add custom remark/rehype plugins or MDX globalComponents, you can use markdown options to achieve:

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  return {
    name: 'plugin-name',
    markdown: {
      // Use js version compiler to support plugins
      mdxRs: false,
      remarkPlugins: [
        // Add custom remark plugin
      ],
      rehypePlugins: [
        // Add custom rehype plugin
      ],
      globalComponents: [
        // Register global components for MDX
      ],
    },
  };
}

extendPageData

  • Type: (pageData: PageData) => void | Promise<void>
plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  return {
    name: 'plugin-name',
    // Extend the page data
    extendPageData(pageData, isProd) {
      // You can add or modify properties on the pageData object
      pageData.a = 1;
    },
  };
}

After extending the page data, you can access the page data through the usePageData hook in the theme.

import { usePageData } from 'rspress';

export function MyComponent() {
  const { page } = usePageData();
  // page.a === 1
  return <div>{page.a}</div>;
}

addPages

  • Type: (config: UserConfig) => AddtionalPage[] | Promise<AddtionalPage[]>

The config parameter is the doc config of rspress.config.ts, and the AddtionalPage type is defined as follows:

interface AddtionalPage {
  routePath: string;
  filepath?: string;
  content?: string;
}

Used to add additional pages, you can return an array in the addPages function, each item in the array is a page config, you can specify the route of the page through routePath, through filepath or content to specify the content of the page. For example:

import path from 'path';
import { RspressPlugin } from '@rspress/shared';

export function docPluginDemo(): RspressPlugin {
  return {
    name: 'add-pages',
    addPages(config, isProd) {
      return [
        //  Support the absolute path of the real file (filepath), which will read the content of md(x) in the disk
        {
          routePath: '/filepath-route',
          filepath: path.join(__dirname, 'blog', 'index.md'),
        },
        //  Support to directly pass in the content of md(x) through the content parameter
        {
          routePath: '/content-route',
          content: '# Demo2',
        },
      ];
    },
  };
}

addPages accepts two parameters, config is the config of the current document site, isProd indicates whether it is a production environment.

routeGenerated

  • Type(routeMeta: RouteMeta[]) => void | Promise<void>

In this hook, you can get all the route meta information. The structure of each route meta information is as follows

export interface RouteMeta {
  // route path
  routePath: string;
  // file absolute path
  absolutePath: string;
  // The page name, as part of the chunk filename
  pageName: string;
  // language of current route
  lang: string;
}

例子:

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  return {
    // plugin name
    name: 'plugin-routes',
    // Hook to execute after route generated
    async routeGenerated(routes, isProd) {
      // Do something here
    },
  };
}

addRuntimeModules

  • Type: (config: UserConfig, isProd: boolean) => Record<string, string> | Promise<Record<string, string>>;

Used to add additional runtime modules. For example, if you want to use some compile-time information in the document, you can achieve this through addRuntimeModules:

plugin.ts
import { RspressPlugin } from '@rspress/shared';

export function pluginForDoc(): RspressPlugin {
  return {
    // Plugin name
    name: 'plugin-name',
    // Add additional runtime modules
    async addRuntimeModules(config, isProd) {
      const fetchSomeData = async () => {
        // Mock asynchronous request
        return { a: 1 };
      };
      const data = await fetchSomeData();
      return {
        'virtual-foo': `export default ${JSON.stringify(data)}`,
      };
    },
  };
}

In this way, you can use the virtual-foo module in the runtime component:

import myData from 'virtual-foo';

export function MyComponent() {
  return <div>{myData.a}</div>;
}
TIP

This hook is executed after the routeGenerated hook.