skills/rspress-custom-theme/SKILL.md
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.
npx skillsauth add rstackjs/agent-skills rspress-custom-themeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
theme/index.tsx if needed (Levels 1A, 3, 4 all need it)@rspress/core/* not rspress/*)| 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 <h1>, <code>, 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 |
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:
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:
// 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.)
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):
/* 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):
// rspress.config.ts
export default defineConfig({
globalStyles: path.join(__dirname, 'styles/custom.css'),
});
Full variable list: Read
references/css-variables.mdfor all available CSS variables with light/dark defaults.
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.
Inject content at specific positions in the layout without replacing built-in components. Override Layout in theme/index.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.mdfor the complete slot list and usage patterns including i18n and MDX component overrides.
Copy a built-in component's source for full replacement. Only use when wrap/slots cannot achieve the customization.
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):
export * from '@rspress/core/theme-original';
export { DocFooter } from './components/DocFooter';
Component list & patterns: Read
references/eject-components.mdfor available components, workflow, and common patterns.
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:
import type { FC, SVGProps } from 'react';
type Icon = FC<SVGProps<SVGSVGElement>> | string;
Example 1 — Replace an icon with a custom SVG component:
// 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:
// 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:
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.
For components that should render on every page without theme overrides:
// rspress.config.ts
export default defineConfig({
globalUIComponents: [
path.join(__dirname, 'components', 'BackToTop.tsx'),
[
path.join(__dirname, 'components', 'Analytics.tsx'),
{ trackingId: '...' },
],
],
});
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.
@rspress/core/theme instead of @rspress/core/theme-original in theme/ files — causes infinite loop.export * from '@rspress/core/theme-original' in theme/index.tsx — breaks all un-overridden components.rspress/theme or @rspress/theme-default — these are v1 paths. v2 uses @rspress/core/theme-original.development
Debug Rstest issues systematically, including performance regressions. First determine whether the slowdown is in build startup or test execution, then run controlled config or code experiments and compare before/after timings.
development
Opinionated Rslib recommendations for modern JS/TS npm package design covering pure ESM, strict TypeScript, explicit exports, small stable APIs, pragmatic dependencies, accurate sideEffects, correct declarations, package validation, provenance, README.md, and AGENTS.md. Use when the user wants to make a JS/TS package more modern, check whether the current package setup is healthy, review package.json/exports/types/dependencies/docs/release readiness, or apply a modern library baseline.
development
Create or update draft GitHub releases for the current project's main GitHub repository, then organize GitHub-generated release notes into user-friendly sections without rewriting release note items. Use for preparing, formatting, categorizing, creating, or updating GitHub release notes or draft releases, including optional highlights when the user asks for them.
tools
Migrate ESLint or other linters to Rslint. Use when asked to replace ESLint flat config, lint scripts, VS Code ESLint settings, inline directives, rules, presets, plugins, or lint dependencies with Rslint equivalents.