rspress-custom-theme

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rspress Custom Theme

Rspress 自定义主题

Guide for customizing Rspress (v2) themes. Rspress offers four levels of customization, from lightest to heaviest. Always prefer the lightest approach that meets the requirement — lighter approaches are more maintainable and survive Rspress upgrades.
Rspress(v2)主题定制指南。Rspress 提供四种定制层级,从最轻量到最重度。请优先选择满足需求的最轻量方案——轻量方案更易维护,且能在 Rspress 版本升级后继续使用。

Workflow

工作流程

  1. Understand the user's goal — what do they want to change? (colors, layout, inject content, replace a component entirely?)
  2. Pick the right level using the decision flow below
  3. Set up
    theme/index.tsx
    if needed (Levels 1A, 3, 4 all need it)
  4. Implement following the patterns in this skill and reference files
  5. Verify the user's Rspress version is v2 (imports use
    @rspress/core/*
    not
    rspress/*
    )
  1. 明确用户目标——用户想要更改什么?(颜色、布局、注入内容、完全替换组件?)
  2. 根据下方决策流程选择合适的层级
  3. 按需设置
    theme/index.tsx
    (层级1A、3、4均需用到)
  4. 参考本指南的模式和参考文件实现定制
  5. 验证用户的 Rspress 版本为 v2(导入路径使用
    @rspress/core/*
    而非
    rspress/*

Decision Flow

决策流程

User wants to...LevelApproach
Change brand colors, fonts, spacing, shadows1CSS variables
Adjust a specific component's style (borders, padding, etc.)2BEM class overrides
Add content around existing components (banners, footers, logos)3Layout slots (wrap)
Override MDX rendering (custom
<h1>
,
<code>
, etc.)
3
components
slot
Wrap the app in a provider (state, analytics, auth)4Eject
Root
Replace built-in icons (logo, GitHub, search, etc.)Icon re-export
Completely replace a built-in component4Eject that component
Add a global floating component (back-to-top, chat widget)
globalUIComponents
config
Control page layout structure (hide sidebar, blank page)Frontmatter
pageType

用户需求层级实现方式
更改品牌颜色、字体、间距、阴影1CSS variables
调整特定组件的样式(边框、内边距等)2BEM 类覆盖
在现有组件周围添加内容(横幅、页脚、Logo)3Layout slots(封装)
覆盖 MDX 渲染逻辑(自定义
<h1>
<code>
等)
3
components
插槽
用提供者包裹整个应用(状态、统计、权限)4Eject
Root
组件
替换内置图标(Logo、GitHub、搜索等)图标重导出
完全替换内置组件4Eject 目标组件
添加全局悬浮组件(返回顶部、聊天窗口)
globalUIComponents
配置
控制页面布局结构(隐藏侧边栏、空白页面)Frontmatter
pageType
属性

theme/index.tsx — The Entry Point

theme/index.tsx — 入口文件

Levels 1A, 3, and 4 all require a
theme/index.tsx
file in the project root (sibling to
docs/
). This is the single entry point for all theme customizations:
text
project/
├── docs/
├── theme/
│   ├── index.tsx        # Theme entry — re-exports + overrides
│   ├── index.css         # CSS variable / BEM overrides (optional)
│   └── components/       # Ejected components (Level 4)
└── rspress.config.ts
Minimal setup:
tsx
// theme/index.tsx
import './index.css'; // optional
export * from '@rspress/core/theme-original';
Critical import rule: Inside
theme/
files, always import from
@rspress/core/theme-original
. The path
@rspress/core/theme
resolves to your own
theme/index.tsx
, which causes circular imports. (In
docs/
MDX files,
@rspress/core/theme
is fine — it correctly points to your custom theme.)

层级1A、3、4均需要在项目根目录(与
docs/
同级)创建
theme/index.tsx
文件。这是所有主题定制的统一入口:
text
project/
├── docs/
├── theme/
│   ├── index.tsx        # 主题入口 — 重导出 + 覆盖
│   ├── index.css         # CSS 变量 / BEM 覆盖(可选)
│   └── components/       # Eject 出的组件(层级4)
└── rspress.config.ts
最小化配置:
tsx
// theme/index.tsx
import './index.css'; // 可选
export * from '@rspress/core/theme-original';
重要导入规则:在
theme/
目录的文件中,务必从
@rspress/core/theme-original
导入。
@rspress/core/theme
路径会指向你自己的
theme/index.tsx
,从而导致循环导入。(在
docs/
目录的 MDX 文件中,使用
@rspress/core/theme
是没问题的——它会正确指向你的自定义主题。)

Level 1: CSS Variables

层级1:CSS Variables

Override CSS custom properties for brand colors, backgrounds, text, code blocks, and more.
Option A
theme/index.css
(use when you also have component overrides in
theme/index.tsx
):
css
/* theme/index.css */
:root {
  --rp-c-brand: #7c3aed;
  --rp-c-brand-light: #8b5cf6;
  --rp-c-brand-dark: #6d28d9;
}
.dark {
  --rp-c-brand: #a78bfa;
}
Option B
globalStyles
(use when you only need CSS changes, no component overrides):
ts
// rspress.config.ts
export default defineConfig({
  globalStyles: path.join(__dirname, 'styles/custom.css'),
});
Full variable list: Read
references/css-variables.md
for all available CSS variables with light/dark defaults.

覆盖 CSS 自定义属性以修改品牌颜色、背景、文字、代码块等样式。
选项A
theme/index.css
(当你同时需要在
theme/index.tsx
中覆盖组件时使用):
css
/* theme/index.css */
:root {
  --rp-c-brand: #7c3aed;
  --rp-c-brand-light: #8b5cf6;
  --rp-c-brand-dark: #6d28d9;
}
.dark {
  --rp-c-brand: #a78bfa;
}
选项B
globalStyles
(仅需要修改 CSS,无需覆盖组件时使用):
ts
// rspress.config.ts
export default defineConfig({
  globalStyles: path.join(__dirname, 'styles/custom.css'),
});
完整变量列表:查看
references/css-variables.md
获取所有带亮色/暗色默认值的可用 CSS 变量。

Level 2: BEM Class Overrides

层级2:BEM 类覆盖

All built-in components follow BEM naming:
.rp-[component]__[element]--[modifier]
.
Common targets:
.rp-nav
,
.rp-link
,
.rp-tabs
,
.rp-codeblock
,
.rp-codeblock__title
,
.rp-nav-menu__item--active
.
Use these in your CSS file for targeted style changes when CSS variables aren't granular enough.

所有内置组件均遵循 BEM 命名规范:
.rp-[component]__[element]--[modifier]
常见目标类:
.rp-nav
.rp-link
.rp-tabs
.rp-codeblock
.rp-codeblock__title
.rp-nav-menu__item--active
当 CSS 变量的粒度不足以满足需求时,可在 CSS 文件中使用这些类进行针对性样式修改。

Level 3: Wrap (Layout Slots)

层级3:封装(Layout Slots)

Inject content at specific positions in the layout without replacing built-in components. Override
Layout
in
theme/index.tsx
:
tsx
// theme/index.tsx
import { Layout as OriginalLayout } from '@rspress/core/theme-original';
export * from '@rspress/core/theme-original';

export function Layout() {
  return (
    <OriginalLayout beforeNavTitle={<MyLogo />} bottom={<CustomFooter />} />
  );
}
Use runtime hooks inside slot components — import from
@rspress/core/runtime
:
useDark()
,
useLang()
,
useVersion()
,
usePage()
,
useSite()
,
useFrontmatter()
,
useI18n()
.
All slots & examples: Read
references/layout-slots.md
for the complete slot list and usage patterns including i18n and MDX component overrides.

在布局的特定位置注入内容,无需替换内置组件。在
theme/index.tsx
中覆盖
Layout
组件:
tsx
// theme/index.tsx
import { Layout as OriginalLayout } from '@rspress/core/theme-original';
export * from '@rspress/core/theme-original';

export function Layout() {
  return (
    <OriginalLayout beforeNavTitle={<MyLogo />} bottom={<CustomFooter />} />
  );
}
可在插槽组件中使用运行时钩子——从
@rspress/core/runtime
导入:
useDark()
useLang()
useVersion()
usePage()
useSite()
useFrontmatter()
useI18n()
所有插槽及示例:查看
references/layout-slots.md
获取完整插槽列表及使用模式,包括国际化和 MDX 组件覆盖。

Level 4: Eject

层级4:Eject 操作

Copy a built-in component's source for full replacement. Only use when wrap/slots cannot achieve the customization.
bash
rspress eject           # list available components
rspress eject DocFooter # eject to theme/components/DocFooter/
Then re-export in
theme/index.tsx
(named export takes precedence over the wildcard):
tsx
export * from '@rspress/core/theme-original';
export { DocFooter } from './components/DocFooter';
Component list & patterns: Read
references/eject-components.md
for available components, workflow, and common patterns.

复制内置组件的源码以实现完全替换。仅当封装/插槽无法满足定制需求时使用。
bash
rspress eject           # 列出可用组件
rspress eject DocFooter # 导出到 theme/components/DocFooter/
然后在
theme/index.tsx
中重导出(命名导出优先级高于通配符导出):
tsx
export * from '@rspress/core/theme-original';
export { DocFooter } from './components/DocFooter';
组件列表及模式:查看
references/eject-components.md
获取可用组件、工作流程及常见模式。

Custom Icons

自定义图标

Rspress has 27 built-in icons used across the UI. You can replace any of them by re-exporting your own icon component with the same name — no ejection needed. This uses the same
theme/index.tsx
mechanism: your named export takes precedence over the wildcard re-export.
Icon type: Each icon is a React component or a URL string:
ts
import type { FC, SVGProps } from 'react';
type Icon = FC<SVGProps<SVGSVGElement>> | string;
Example 1 — Replace an icon with a custom SVG component:
tsx
// theme/index.tsx
export * from '@rspress/core/theme-original';

// Named export overrides the wildcard — replaces the GitHub icon site-wide
export const IconGithub = (props: React.SVGProps<SVGSVGElement>) => (
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
    <path d="M12 2C6.477 2 2 6.484 2 12.017c0 ..." fill="currentColor" />
  </svg>
);
Example 2 — Use an SVGR import:
tsx
// theme/index.tsx
export * from '@rspress/core/theme-original';

import CustomGithubIcon from './icons/github.svg?react';
export const IconGithub = CustomGithubIcon;
Using
SvgWrapper
in MDX or custom components
:
mdx
import { SvgWrapper, IconGithub } from '@rspress/core/theme';

<SvgWrapper icon={IconGithub} width={24} height={24} />
Available icons:
IconArrowDown
,
IconArrowRight
,
IconClose
,
IconCopy
,
IconDeprecated
,
IconDown
,
IconEdit
,
IconEmpty
,
IconExperimental
,
IconExternalLink
,
IconFile
,
IconGithub
,
IconGitlab
,
IconHeader
,
IconJump
,
IconLink
,
IconLoading
,
IconMenu
,
IconMoon
,
IconScrollToTop
,
IconSearch
,
IconSmallMenu
,
IconSuccess
,
IconSun
,
IconTitle
,
IconWrap
,
IconWrapped
.
Source: See the icons source for default implementations.

Rspress 包含27个用于 UI 各处的内置图标。你可以通过重导出同名自定义图标组件来替换任意内置图标——无需执行 eject 操作。这使用了与
theme/index.tsx
相同的机制:你的命名导出优先级高于通配符重导出。
图标类型:每个图标都是 React 组件或 URL 字符串:
ts
import type { FC, SVGProps } from 'react';
type Icon = FC<SVGProps<SVGSVGElement>> | string;
示例1 — 使用自定义 SVG 组件替换图标:
tsx
// theme/index.tsx
export * from '@rspress/core/theme-original';

// 命名导出覆盖通配符导出——全站替换 GitHub 图标
export const IconGithub = (props: React.SVGProps<SVGSVGElement>) => (
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
    <path d="M12 2C6.477 2 2 6.484 2 12.017c0 ..." fill="currentColor" />
  </svg>
);
示例2 — 使用 SVGR 导入:
tsx
// theme/index.tsx
export * from '@rspress/core/theme-original';

import CustomGithubIcon from './icons/github.svg?react';
export const IconGithub = CustomGithubIcon;
在 MDX 或自定义组件中使用
SvgWrapper
mdx
import { SvgWrapper, IconGithub } from '@rspress/core/theme';

<SvgWrapper icon={IconGithub} width={24} height={24} />
可用图标
IconArrowDown
IconArrowRight
IconClose
IconCopy
IconDeprecated
IconDown
IconEdit
IconEmpty
IconExperimental
IconExternalLink
IconFile
IconGithub
IconGitlab
IconHeader
IconJump
IconLink
IconLoading
IconMenu
IconMoon
IconScrollToTop
IconSearch
IconSmallMenu
IconSuccess
IconSun
IconTitle
IconWrap
IconWrapped
源码:查看 图标源码 获取默认实现。

Global UI Components

全局 UI 组件

For components that should render on every page without theme overrides:
ts
// rspress.config.ts
export default defineConfig({
  globalUIComponents: [
    path.join(__dirname, 'components', 'BackToTop.tsx'),
    [
      path.join(__dirname, 'components', 'Analytics.tsx'),
      { trackingId: '...' },
    ],
  ],
});

如需在所有页面渲染组件且无需修改主题:
ts
// rspress.config.ts
export default defineConfig({
  globalUIComponents: [
    path.join(__dirname, 'components', 'BackToTop.tsx'),
    [
      path.join(__dirname, 'components', 'Analytics.tsx'),
      { trackingId: '...' },
    ],
  ],
});

Page Types

页面类型

Control layout per page via frontmatter
pageType
:
ValueDescription
home
Home page with navbar
doc
Standard doc with sidebar and outline
doc-wide
Doc without sidebar/outline
custom
Custom content with navbar only
blank
Custom content without navbar
404
404 error page
Fine-grained: set
navbar: false
,
sidebar: false
,
outline: false
,
footer: false
individually.

通过 Frontmatter 的
pageType
属性控制单页面布局:
描述
home
带导航栏的首页
doc
带侧边栏和大纲的标准文档页
doc-wide
无侧边栏/大纲的文档页
custom
仅带导航栏的自定义内容页
blank
无导航栏的自定义内容页
404
404 错误页
更精细的控制:可单独设置
navbar: false
sidebar: false
outline: false
footer: false

Common Pitfalls

常见陷阱

  • Circular import: Using
    @rspress/core/theme
    instead of
    @rspress/core/theme-original
    in
    theme/
    files — causes infinite loop.
  • Eject over-use: Ejecting when a Layout slot or CSS variable would suffice — creates upgrade burden.
  • Missing re-export: Forgetting
    export * from '@rspress/core/theme-original'
    in
    theme/index.tsx
    — breaks all un-overridden components.
  • v1 imports: Using
    rspress/theme
    or
    @rspress/theme-default
    — these are v1 paths. v2 uses
    @rspress/core/theme-original
    .
  • 循环导入:在
    theme/
    文件中使用
    @rspress/core/theme
    而非
    @rspress/core/theme-original
    ——会导致无限循环。
  • 过度使用 Eject:在 Layout 插槽或 CSS 变量可满足需求时仍执行 Eject——会增加版本升级的负担。
  • 遗漏重导出:在
    theme/index.tsx
    中忘记添加
    export * from '@rspress/core/theme-original'
    ——会导致所有未覆盖的组件失效。
  • v1 导入路径:使用
    rspress/theme
    @rspress/theme-default
    ——这些是 v1 路径,v2 使用
    @rspress/core/theme-original

Reference

参考链接