--- name: rspress-custom-theme description: Customize Rspress themes using CSS variables, Layout slots, component wrapping, or component ejection. Use when a user wants to change the look and feel of an Rspress site, override theme components, add custom navigation/sidebar/footer content, inject global providers, or modify the default Rspress theme in any way. Also use when a user mentions theme/index.tsx, Layout slots, BEM class overrides, or rspress eject. --- # Rspress Custom Theme 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. ## 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/*`) ## Decision Flow | User wants to... | Level | Approach | | ---------------------------------------------------------------- | ----- | --------------------------- | | Change brand colors, fonts, spacing, shadows | 1 | CSS variables | | Adjust a specific component's style (borders, padding, etc.) | 2 | BEM class overrides | | Add content around existing components (banners, footers, logos) | 3 | Layout slots (wrap) | | Override MDX rendering (custom `

`, ``, etc.) | 3 | `components` slot | | Wrap the app in a provider (state, analytics, auth) | 4 | Eject `Root` | | Replace built-in icons (logo, GitHub, search, etc.) | — | Icon re-export | | Completely replace a built-in component | 4 | Eject that component | | Add a global floating component (back-to-top, chat widget) | — | `globalUIComponents` config | | Control page layout structure (hide sidebar, blank page) | — | Frontmatter `pageType` | --- ## theme/index.tsx — The Entry Point 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.) --- ## Level 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. --- ## Level 2: BEM Class Overrides 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. --- ## Level 3: Wrap (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 ( } bottom={} /> ); } ``` 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. --- ## Level 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. --- ## 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> | 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) => ( ); ``` **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'; ``` **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](https://github.com/web-infra-dev/rspress/blob/main/packages/core/src/theme/icons.ts) for default implementations. --- ## Global UI Components 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: '...' }, ], ], }); ``` --- ## Page Types Control layout per page via frontmatter `pageType`: | Value | Description | | ---------- | ------------------------------------- | | `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. --- ## 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`. ## Reference - Custom theme guide: - CSS variables: - Layout component: - Built-in icons: - Built-in hooks: - CLI commands (eject):