Shared UI component library (@rstackjs/doc-ui) for Rstack websites (rspack.rs, rsbuild.rs, rspress.rs).
- React 19 + TypeScript 7 (strict mode)
- Toolchain: Rstack CLI (
rs lib, Rstest, Rslint, and formatting) - Styling: SCSS Modules (
.module.scss) - Animation: Framer Motion, Lottie
- UI: Ant Design 6
- Dev: Storybook 10
For Rstack CLI behavior:
- Read the docs linked from
node_modules/rstack/docs/llms.txtwhen needed - Online docs: https://rstack.rs/llms.txt
- Run
rs -hfor CLI help
# Development
pnpm dev # Start Storybook (port 6006)
# Build and test
pnpm build # Build the library with Rstack CLI
pnpm build:watch # Build in watch mode
pnpm test # Run Rstest
# Checks and formatting
pnpm check # Run lint and formatting checks
pnpm lint # Run Rslint
pnpm format # Format filessrc/
├── announcement/ # Announcement banner
├── antd/ # Ant Design wrappers
├── background-image/ # Background component
├── benchmark/ # Benchmark display
├── built-with-rspack/ # Showcase component
├── fully-featured/ # Feature list
├── hero/ # Hero section
├── nav-icon/ # Nav icon with popover
├── section-style/ # Section utilities
├── tool-stack/ # Tool stack display
├── why-rspack/ # Feature cards
├── env.d.ts # Type declarations
└── shared.tsx # Shared utilities
stories/ # Storybook stories
- Single quotes, trailing commas (
all), no parens for single arrow params
- Rslint uses TypeScript and React recommended rules
@typescript-eslint/no-explicit-any: offreact/react-in-jsx-scope: off
- Strict mode, use
typeimports:import type { FC } from 'react' - Path alias:
@/*→./src/*
- External packages (react, antd, framer-motion)
- Internal components (relative)
- Styles (
.module.scss) - Assets (JSON, images)
import type { FC } from 'react';
import { useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { ProgressBar } from './ProgressBar';
import styles from './index.module.scss';- Components:
PascalCase(Hero,NavIcon) - Hooks:
useCamelCase(useMouseMove) - Props:
ComponentNameProps(HeroProps) - Styles:
index.module.scssper component
import type { FC } from 'react';
import styles from './index.module.scss';
export type MyComponentProps = {
title: string;
active?: boolean;
};
export const MyComponent: FC<MyComponentProps> = ({
title,
active = false,
}) => {
return <div className={styles.root}>{title}</div>;
};- SCSS Modules only (
.module.scss) - Compose:
${styles.btn} ${styles.btnPrimary} - Root class:
styles.root
- Functional components with hooks
- SCSS Modules for styling
- Export types with components
- Semantic HTML (
section,button) - Default values for optional props
memo()for expensive components
- Inline styles (except dynamic values)
- CSS-in-JS (emotion, styled-components)
- Hard-coded colors
- Class components
- Heavy deps without consideration
// stories/Hero.stories.tsx
import { Hero } from '@rstackjs/doc-ui/hero';
import './index.scss';
export const HeroStory = () => <Hero onClickGetStarted={() => {}} />;
export default { title: 'Hero' };Rstack CLI installs repository hooks from .rstack/hooks. The pre-commit hook runs rs staged.
- Create
src/component-name/index.tsx - Add
src/component-name/index.module.scss - Add an entry in
rstack.config.ts - Add an export in
package.jsonexports - Create
stories/ComponentName.stories.tsx
Externalized (consumers provide): react, react-dom, framer-motion
Peer deps: antd, lottie-web, react-intersection-observer