docs/cheatsheet.en-US.md
Create a project:
git clone --depth 1 https://github.com/ant-design/ant-design-pro.git my-project
cd my-project
npm install
The project offers two modes:
Switch to simple mode:
git add -A && git commit -m "chore: save before simple" # Commit first to allow revert
npm run simple # Remove demo pages and unused deps
npm install # Update dependencies
π‘ Start with full mode to learn the project structure, then switch to simple mode for development.
Directory structure:
βββ config/ # Configuration (routes, proxy, theme)
β βββ config.ts # Main config
β βββ routes.ts # Route definitions
β βββ defaultSettings.ts # Layout & theme settings
β βββ proxy.ts # Dev proxy config
βββ mock/ # Mock data
βββ src/
β βββ components/ # Shared components
β βββ locales/ # i18n resources
β βββ models/ # Global data models
β βββ services/ # API service layer
β βββ utils/ # Utility functions
β βββ access.ts # Permission definitions
β βββ app.tsx # Runtime configuration
βββ docs/ # Project documentation
βββ types/ # Type declarations
Common commands:
| Command | Description |
|---|---|
npm start | Start dev server (UMI_ENV=dev, with Mock) |
npm run dev | Start dev server (UMI_ENV=dev, no Mock) |
npm run start:no-mock | Start without Mock |
npm run start:pre | Pre-production environment |
npm run start:test | Test environment |
npm run build | Build for production |
npm run preview | Preview built output (run npm run build first, port 8000) |
npm run preview:build | Build and preview (port 8000) |
npm run deploy | Build and deploy to GitHub Pages |
npm run analyze | Analyze bundle size |
npm run lint | Lint (Biome + TypeScript) |
npm run biome | Auto-fix with Biome |
npm test | Run tests |
npm run test:coverage | Test with coverage |
npm run test:update | Update test snapshots |
npm run tsc | Type check without emitting |
npm run i18n-remove | Remove i18n wrappers (locale=zh-CN) |
npm run record | Record request data for login scene |
npm run openapi | Generate API code from OpenAPI schema |
npm run simple | Strip demo pages and unused deps |
π‘
UMI_ENVswitches environment configs, mapping to different proxy rules inconfig/proxy.ts.
π‘
npm run simpleremoves demo pages (dashboard, form, list etc.) and unused dependencies (plots, etc.), replacing with minimal routes. Ideal for starting from scratch. Commit your code first so you can revert if needed.
Build tool: This project uses utoopack (a next-gen bundler powered by Turbopack) as the default build tool, configured via the utoopack field in config/config.ts. utoopack is Webpack-compatible and supports module.rules for custom loaders.
β See umi Getting Started, utoo Docs
Route config is in config/routes.ts:
export default [
{
path: '/welcome',
name: 'welcome', // maps to menu.welcome i18n key
icon: 'home',
component: './Welcome',
},
{
path: '/admin',
name: 'admin',
icon: 'crown',
access: 'canAdmin', // route-level access control
routes: [...],
},
{ path: '/', redirect: '/dashboard/analysis' },
{ component: '404', path: './*' },
];
Route navigation:
import { useNavigate, useParams, useLocation } from '@umijs/max';
const navigate = useNavigate();
navigate('/dashboard'); // navigate
navigate(-1); // go back
const { id } = useParams(); // dynamic param /user/:id
const location = useLocation(); // current route info
Menu & access: The access field in route config controls menu visibility β unauthorized routes won't appear in the menu.
π‘ The
namefield is automatically mapped tomenu.xxxi18n keys. Configure translations insrc/locales/.
β See umi Routes, Umi Max Layout & Menu
ProLayout config is in config/defaultSettings.ts:
export default {
navTheme: 'light', // nav theme: light / dark
colorPrimary: '#1890ff', // primary color
layout: 'mix', // layout mode: side / top / mix
contentWidth: 'Fluid', // content width: Fluid / Fixed
fixSiderbar: true, // fixed sidebar
};
Layout modes:
side β Side navigationtop β Top navigationmix β Top + side mixed navigationPage container:
import { PageContainer } from '@ant-design/pro-components';
const Page = () => (
<PageContainer
header={{ title: 'Page Title' }}
content="Page description"
>
</PageContainer>
);
Custom areas: Top-right src/components/RightContent, footer src/components/Footer.
β See Umi Max Layout & Menu
useModel β lightweight global state: Create a file in src/models/ to auto-register:
// src/models/counter.ts
import { useState } from 'react';
export default function useCounter() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
return { count, increment };
}
// Use in any component
import { useModel } from '@umijs/max';
const { count, increment } = useModel('counter');
useRequest β data fetching:
import { useRequest } from '@umijs/max';
const { data, loading, error } = useRequest(getUserInfo);
React Query β server state management:
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Query
const { data, isLoading } = useQuery({
queryKey: ['user', id],
queryFn: () => getUser(id),
});
// Mutation
const mutation = useMutation({
mutationFn: updateUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['user'] });
},
});
Initial state β getInitialState: Define in src/app.tsx, accessible globally:
// src/app.tsx
export async function getInitialState() {
const currentUser = await fetchUserInfo();
return { currentUser };
}
// Use in components
import { useModel } from '@umijs/max';
const { initialState } = useModel('@@initialState');
π‘
getInitialStateruns once on app startup, ideal for fetching global info (user identity, permissions).
β See Umi Max Data Flow
Request config is in src/app.tsx:
export const request: RequestConfig = {
baseURL: 'https://api.example.com',
timeout: 10000,
requestInterceptors: [], // request interceptors
responseInterceptors: [], // response interceptors
};
Error handling is in src/requestErrorConfig.ts, customize error code mapping and notification logic.
Using request:
import { request } from '@umijs/max';
// GET
const data = await request('/api/users', { params: { page: 1 } });
// POST
await request('/api/users', { method: 'POST', data: { name: 'test' } });
OpenAPI code generation:
npm run openapi
Auto-generates API calling code under src/services/ based on config/oneapi.json.
π‘ Generated code uses
import { request } from '@umijs/max'directly β no manual wrapping needed.
β See Umi Max Request
Define permissions in src/access.ts:
export default function access(initialState: { currentUser?: API.CurrentUser }) {
const { currentUser } = initialState;
return {
canAdmin: currentUser?.access === 'admin',
canUser: !!currentUser,
};
}
Route-level access: Add access field in route config:
{ path: '/admin', access: 'canAdmin' }
Component-level access:
import { Access, useAccess } from '@umijs/max';
// Declarative
<Access accessible={access.canAdmin}>
<AdminPanel />
</Access>
// Imperative
const access = useAccess();
if (access.canAdmin) { /* ... */ }
β See Umi Max Access
Config in config/config.ts:
locale: {
default: 'zh-CN',
antd: true, // sync antd component locale
baseNavigator: true, // follow browser language
},
File structure:
src/locales/
βββ zh-CN.ts # Chinese entry
βββ zh-CN/
β βββ menu.ts # Menu translations
β βββ pages.ts # Page translations
β βββ ...
βββ en-US.ts # English entry
βββ en-US/
βββ ...
Usage:
import { useIntl, FormattedMessage } from '@umijs/max';
// Hook
const intl = useIntl();
intl.formatMessage({ id: 'menu.welcome' });
// Component
<FormattedMessage id="menu.welcome" />
Switch locale:
import { setLocale } from '@umijs/max';
setLocale('en-US', false); // false = no page reload
β See Umi Max i18n
CSS Modules: Name files *.module.less or *.module.css:
/* example.module.less */
.container { padding: 24px; }
.title { font-size: 16px; }
import styles from './example.module.less';
<div className={styles.container} />
antd-style (CSS-in-JS):
import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token, css }) => ({
card: css`
background: ${token.colorBgContainer};
border-radius: ${token.borderRadiusLG}px;
`,
}));
const { styles } = useStyles();
<div className={styles.card} />
Tailwind CSS (v4): Use directly in className:
<div className="flex items-center gap-4 p-6 rounded-lg bg-white dark:bg-[#141414]" />
Dynamic theme: Set in config/config.ts antd config:
antd: {
configProvider: {
theme: {
token: {
colorPrimary: '#1890ff',
borderRadius: 6,
},
},
},
},
Use SettingDrawer in dev mode to switch themes in real-time.
π‘ Three styling approaches can coexist: Tailwind for layout, CSS Modules for component styles, antd-style when consuming theme tokens.
β See umi Styling, Umi Max antd Dynamic Theme
Jest testing:
npm test # Run all tests
npm run test:coverage # With coverage report
npm run test:update # Update snapshots
Test files go next to the component, named *.test.ts(x).
Mock data: Create files in mock/:
// mock/user.ts
export default {
'GET /api/currentUser': { name: 'Serati Ma', access: 'admin' },
'POST /api/login': (req, res) => { res.end('ok'); },
};
Umi auto-registers mocks, active in dev mode.
Proxy config is in config/proxy.ts:
export default {
dev: {
'/api/': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
};
π‘ Use
MOCK=noneto skip mock and proxy to backend:npm run start:no-mock.
β See umi Testing, umi Mock
Q: How to disable Mock?
npm run start:no-mock or cross-env MOCK=none max dev
Q: How to change the primary color?
Edit colorPrimary in config/defaultSettings.ts. Use SettingDrawer for live preview in dev mode.
Q: How to add a new page?
src/pages/ 2. Add route in config/routes.ts 3. Add menu translation in src/locales/ (if needed)Q: How to add global state?
Create a file in src/models/ exporting a custom Hook, then use useModel('filename') in components.
Q: How to deploy?
npm run build generates dist/. Deploy to any static file server. Set publicPath for non-root deployments. npm run deploy builds and publishes to GitHub Pages automatically (pushes to gh-pages branch).
Q: How to use OpenAPI code generation?
openAPI in config/config.ts 2. Run npm run openapi 3. Code is auto-generated under src/services/β See umi FAQ