docs-sandpack

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Sandpack Patterns

Sandpack 使用模式

Quick Start Template

快速开始模板

Most examples are single-file. Copy this and modify:
mdx
<Sandpack>

` ` `js
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState(0);

  return (
    <button onClick={() => setValue(value + 1)}>
      Clicked {value} times
    </button>
  );
}
` ` `

</Sandpack>

大多数示例为单文件形式。可复制以下模板进行修改:
mdx
<Sandpack>

` ` `js
import { useState } from 'react';

export default function Example() {
  const [value, setValue] = useState(0);

  return (
    <button onClick={() => setValue(value + 1)}>
      Clicked {value} times
    </button>
  );
}
` ` `

</Sandpack>

File Naming

文件命名规则

PatternUsage
```js
Main file (no prefix)
```js src/FileName.js
Supporting files
```js src/File.js active
Active file (reference pages)
```js src/data.js hidden
Hidden files
```css
CSS styles
```json package.json
External dependencies
Critical: Main file must have
export default
.
写法模式用途
```js
主文件(无前缀)
```js src/FileName.js
辅助文件
```js src/File.js active
激活文件(参考页面)
```js src/data.js hidden
隐藏文件
```css
CSS样式文件
```json package.json
外部依赖配置
重点注意: 主文件必须包含
export default

Line Highlighting

代码行高亮

mdx
```js {2-4}
function Example() {
  // Lines 2-4
  // will be
  // highlighted
  return null;
}
mdx
```js {2-4}
function Example() {
  // 第2-4行
  // 将被
  // 高亮显示
  return null;
}

Code References (numbered callouts)

代码引用(编号标注)

mdx
```js [[1, 4, "age"], [2, 4, "setAge"]]
// Creates numbered markers pointing to "age" and "setAge" on line 4
mdx
```js [[1, 4, "age"], [2, 4, "setAge"]]
// 在第4行创建指向"age"和"setAge"的编号标记

Expected Errors (intentionally broken examples)

预期错误(故意编写的错误示例)

mdx
```js {expectedErrors: {'react-compiler': [7]}}
// Line 7 shows as expected error
mdx
```js {expectedErrors: {'react-compiler': [7]}}
// 第7行会显示为预期错误

Multi-File Example

多文件示例

mdx
<Sandpack>

```js src/App.js
import Gallery from './Gallery.js';

export default function App() {
  return <Gallery />;
}
js
export default function Gallery() {
  return <h1>Gallery</h1>;
}
css
h1 { color: purple; }
</Sandpack> ```
mdx
<Sandpack>

```js src/App.js
import Gallery from './Gallery.js';

export default function App() {
  return <Gallery />;
}
js
export default function Gallery() {
  return <h1>Gallery</h1>;
}
css
h1 { color: purple; }
</Sandpack> ```

External Dependencies

外部依赖配置

mdx
<Sandpack>

```js
import { useImmer } from 'use-immer';
// ...
json
{
  "dependencies": {
    "immer": "1.7.3",
    "use-immer": "0.5.1",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest"
  }
}
</Sandpack> ```
mdx
<Sandpack>

```js
import { useImmer } from 'use-immer';
// ...
json
{
  "dependencies": {
    "immer": "1.7.3",
    "use-immer": "0.5.1",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest"
  }
}
</Sandpack> ```

Code Style in Sandpack (Required)

Sandpack 代码样式要求(强制遵循)

Sandpack examples are held to strict code style standards:
  1. Function declarations for components (not arrows)
  2. e
    for event parameters
  3. Single quotes in JSX
  4. const
    unless reassignment needed
  5. Spaces in destructuring:
    ({ props })
    not
    ({props})
  6. Two-line createRoot: separate declaration and render call
  7. Multiline if statements: always use braces
Sandpack示例需遵循严格的代码样式标准:
  1. 组件使用函数声明(而非箭头函数)
  2. 事件参数使用**
    e
    **表示
  3. JSX中使用单引号
  4. 除非需要重新赋值,否则使用**
    const
    **
  5. 解构赋值中添加空格:
    ({ props })
    而非
    ({props})
  6. 分两行编写createRoot:将声明和渲染调用分开
  7. 多行if语句:始终使用大括号

Don't Create Hydration Mismatches

避免出现 Hydration 不匹配问题

Sandpack examples must produce the same output on server and client:
js
// 🚫 This will cause hydration warnings
export default function App() {
  const isClient = typeof window !== 'undefined';
  return <div>{isClient ? 'Client' : 'Server'}</div>;
}
Sandpack示例必须在服务端和客户端生成相同的输出:
js
// 🚫 这会导致Hydration警告
export default function App() {
  const isClient = typeof window !== 'undefined';
  return <div>{isClient ? 'Client' : 'Server'}</div>;
}

Use Ref for Non-Rendered State

针对非渲染状态使用Ref

js
// 🚫 Don't trigger re-renders for non-visual state
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);

// ✅ Use ref instead
const mounted = useRef(false);
useEffect(() => { mounted.current = true; }, []);
js
// 🚫 不要为非视觉状态触发重渲染
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);

// ✅ 改用Ref
const mounted = useRef(false);
useEffect(() => { mounted.current = true; }, []);

forwardRef and memo Patterns

forwardRef 和 memo 使用模式

forwardRef - Use Named Function

forwardRef - 使用命名函数

js
// ✅ Named function for DevTools display name
const MyInput = forwardRef(function MyInput(props, ref) {
  return <input {...props} ref={ref} />;
});

// 🚫 Anonymous loses name
const MyInput = forwardRef((props, ref) => { ... });
js
// ✅ 命名函数可在DevTools中显示组件名称
const MyInput = forwardRef(function MyInput(props, ref) {
  return <input {...props} ref={ref} />;
});

// 🚫 匿名函数会丢失组件名称
const MyInput = forwardRef((props, ref) => { ... });

memo - Use Named Function

memo - 使用命名函数

js
// ✅ Preserves component name
const Greeting = memo(function Greeting({ name }) {
  return <h1>Hello, {name}</h1>;
});
js
// ✅ 保留组件名称
const Greeting = memo(function Greeting({ name }) {
  return <h1>Hello, {name}</h1>;
});

Line Length

行长度规范

  • Prose: ~80 characters
  • Code: ~60-70 characters
  • Break long lines to avoid horizontal scrolling
  • 说明文字:约80个字符
  • 代码:约60-70个字符
  • 长行需换行,避免横向滚动

Anti-Patterns

反模式示例

PatternProblemFix
const Comp = () => {}
Not standard
function Comp() {}
onClick={(event) => ...}
Conflicts with global
onClick={(e) => ...}
useState
for non-rendered values
Re-rendersUse
useRef
Reading
window
during render
Hydration mismatchCheck in useEffect
Single-line if without bracesHarder to debugUse multiline with braces
Chained
createRoot().render()
Less clearTwo statements
//...
without space
Inconsistent
// ...
with space
TabsInconsistent2 spaces
ReactDOM.render
DeprecatedUse
createRoot
Fake package namesConfusingUse
'./your-storage-layer'
PropsWithChildren
Outdated
children?: ReactNode
Missing
key
in lists
WarningsAlways include key
模式问题修复方案
const Comp = () => {}
不符合标准
function Comp() {}
onClick={(event) => ...}
与全局变量冲突
onClick={(e) => ...}
为非渲染值使用
useState
触发不必要重渲染使用
useRef
渲染时读取
window
Hydration不匹配在useEffect中检查
无大括号的单行if语句调试难度高使用带大括号的多行写法
链式调用
createRoot().render()
可读性差拆分为两个语句
//...
无空格
格式不一致
// ...
带空格
使用制表符格式不一致使用2个空格
ReactDOM.render
已废弃使用
createRoot
虚假包名易混淆使用
'./your-storage-layer'
PropsWithChildren
已过时
children?: ReactNode
列表中缺少
key
触发警告始终添加key

Additional Code Quality Rules

额外代码质量规则

Always Include Keys in Lists

列表中始终包含Key

js
// ✅ Correct
{items.map(item => <li key={item.id}>{item.name}</li>)}

// 🚫 Wrong - missing key
{items.map(item => <li>{item.name}</li>)}
js
// ✅ 正确写法
{items.map(item => <li key={item.id}>{item.name}</li>)}

// 🚫 错误写法 - 缺少key
{items.map(item => <li>{item.name}</li>)}

Use Realistic Import Paths

使用真实感的导入路径

js
// ✅ Correct - descriptive path
import { fetchData } from './your-data-layer';

// 🚫 Wrong - looks like a real npm package
import { fetchData } from 'cool-data-lib';
js
// ✅ 正确写法 - 路径具有描述性
import { fetchData } from './your-data-layer';

// 🚫 错误写法 - 看起来像真实的npm包
import { fetchData } from 'cool-data-lib';

Console.log Labels

Console.log 添加标签

js
// ✅ Correct - labeled for clarity
console.log('User:', user);
console.log('Component Stack:', errorInfo.componentStack);

// 🚫 Wrong - unlabeled
console.log(user);
js
// ✅ 正确写法 - 添加标签提升可读性
console.log('User:', user);
console.log('Component Stack:', errorInfo.componentStack);

// 🚫 错误写法 - 无标签
console.log(user);

Keep Delays Reasonable

保持合理的延迟时间

js
// ✅ Correct - 1-1.5 seconds
setTimeout(() => setLoading(false), 1000);

// 🚫 Wrong - too long, feels sluggish
setTimeout(() => setLoading(false), 3000);
js
// ✅ 正确写法 - 1-1.5秒
setTimeout(() => setLoading(false), 1000);

// 🚫 错误写法 - 时间过长,体验卡顿
setTimeout(() => setLoading(false), 3000);

Updating Line Highlights

更新行高亮标记

When modifying code in examples with line highlights (
{2-4}
), always update the highlight line numbers to match the new code. Incorrect line numbers cause rendering crashes.
当修改带有行高亮标记(
{2-4}
)的示例代码时,务必更新高亮行号以匹配新代码。错误的行号会导致渲染崩溃。

File Name Conventions

文件命名约定

  • Capitalize file names for component files:
    Gallery.js
    not
    gallery.js
  • After initially explaining files are in
    src/
    , refer to files by name only:
    Gallery.js
    not
    src/Gallery.js
  • 组件文件首字母大写:
    Gallery.js
    而非
    gallery.js
  • 在首次说明文件位于
    src/
    目录后,后续仅使用文件名指代:
    Gallery.js
    而非
    src/Gallery.js

Naming Conventions in Code

代码中的命名约定

Components: PascalCase
  • Profile
    ,
    Avatar
    ,
    TodoList
    ,
    PackingList
State variables: Destructured pattern
  • const [count, setCount] = useState(0)
  • Booleans:
    [isOnline, setIsOnline]
    ,
    [isPacked, setIsPacked]
  • Status strings:
    'typing'
    ,
    'submitting'
    ,
    'success'
    ,
    'error'
Event handlers:
  • handleClick
    ,
    handleSubmit
    ,
    handleAddTask
Props for callbacks:
  • onClick
    ,
    onChange
    ,
    onAddTask
    ,
    onSelect
Custom Hooks:
  • useOnlineStatus
    ,
    useChatRoom
    ,
    useFormInput
Reducer actions:
  • Past tense:
    'added'
    ,
    'changed'
    ,
    'deleted'
  • Snake_case compounds:
    'changed_selection'
    ,
    'sent_message'
Updater functions: Single letter
  • setCount(n => n + 1)
组件: 大驼峰式(PascalCase)
  • Profile
    ,
    Avatar
    ,
    TodoList
    ,
    PackingList
状态变量: 解构模式
  • const [count, setCount] = useState(0)
  • 布尔值:
    [isOnline, setIsOnline]
    ,
    [isPacked, setIsPacked]
  • 状态字符串:
    'typing'
    ,
    'submitting'
    ,
    'success'
    ,
    'error'
事件处理函数:
  • handleClick
    ,
    handleSubmit
    ,
    handleAddTask
回调函数Props:
  • onClick
    ,
    onChange
    ,
    onAddTask
    ,
    onSelect
自定义Hooks:
  • useOnlineStatus
    ,
    useChatRoom
    ,
    useFormInput
Reducer动作:
  • 过去式:
    'added'
    ,
    'changed'
    ,
    'deleted'
  • 蛇形命名复合词:
    'changed_selection'
    ,
    'sent_message'
更新器函数: 单字母参数
  • setCount(n => n + 1)

Pedagogical Code Markers

教学用代码标记

Wrong vs right code:
js
// 🔴 Avoid: redundant state and unnecessary Effect
// ✅ Good: calculated during rendering
Console.log for lifecycle teaching:
js
console.log('✅ Connecting...');
console.log('❌ Disconnected.');
错误与正确代码对比:
js
// 🔴 避免:冗余状态和不必要的Effect
// ✅ 推荐:在渲染时计算
用于生命周期教学的Console.log:
js
console.log('✅ 连接中...');
console.log('❌ 已断开连接。');

Server/Client Labeling

服务端/客户端组件标记

js
// Server Component
async function Notes() {
  const notes = await db.notes.getAll();
}

// Client Component
"use client"
export default function Expandable({children}) {
  const [expanded, setExpanded] = useState(false);
}
js
// 服务端组件
async function Notes() {
  const notes = await db.notes.getAll();
}

// 客户端组件
"use client"
export default function Expandable({children}) {
  const [expanded, setExpanded] = useState(false);
}

Bundle Size Annotations

包体积注释

js
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)

js
import marked from 'marked'; // 35.9K(压缩后11.2K)
import sanitizeHtml from 'sanitize-html'; // 206K(压缩后63.3K)

Sandpack Example Guidelines

Sandpack 示例指南

Package.json Rules

Package.json 规则

Include package.json when:
  • Using external npm packages (immer, remarkable, leaflet, toastify-js, etc.)
  • Demonstrating experimental/canary React features
  • Requiring specific React versions (
    react: beta
    ,
    react: 19.0.0-rc-*
    )
Omit package.json when:
  • Example uses only built-in React features
  • No external dependencies needed
  • Teaching basic hooks, state, or components
Always mark package.json as hidden:
mdx
```json package.json hidden
{
  "dependencies": {
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "immer": "1.7.3"
  }
}

**Version conventions:**
- Use `"latest"` for stable features
- Use exact versions only when compatibility requires it
- Include minimal dependencies (just what the example needs)
需要包含package.json的场景:
  • 使用外部npm包(immer、remarkable、leaflet、toastify-js等)
  • 演示React实验性/预览版特性
  • 需要指定特定React版本(
    react: beta
    ,
    react: 19.0.0-rc-*
可以省略package.json的场景:
  • 示例仅使用React内置特性
  • 无需外部依赖
  • 教学基础Hooks、状态管理或组件
始终将package.json标记为隐藏:
mdx
```json package.json hidden
{
  "dependencies": {
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "immer": "1.7.3"
  }
}

**版本约定:**
- 稳定特性使用`"latest"`
- 仅在兼容性要求时使用精确版本
- 仅包含示例所需的最小依赖

Hidden File Patterns

隐藏文件规则

Always hide these file types:
File TypeReason
package.json
Configuration not the teaching point
sandbox.config.json
Sandbox setup is boilerplate
public/index.html
HTML structure not the focus
src/data.js
When it contains sample/mock data
src/api.js
When showing API usage, not implementation
src/styles.css
When styling is not the lesson
src/router.js
Supporting infrastructure
src/actions.js
Server action implementation details
Rationale:
  • Reduces cognitive load
  • Keeps focus on the primary concept
  • Creates cleaner, more focused examples
Example:
mdx
```js src/data.js hidden
export const items = [
  { id: 1, name: 'Item 1' },
  { id: 2, name: 'Item 2' },
];
undefined
始终隐藏以下类型的文件:
文件类型原因
package.json
配置信息非教学重点
sandbox.config.json
Sandbox配置为样板代码
public/index.html
HTML结构非关注重点
src/data.js
包含示例/模拟数据时
src/api.js
展示API用法而非实现时
src/styles.css
样式非教学内容时
src/router.js
辅助基础设施代码
src/actions.js
服务端动作实现细节
设计思路:
  • 降低认知负担
  • 保持焦点在核心概念上
  • 创建更简洁、聚焦的示例
示例:
mdx
```js src/data.js hidden
export const items = [
  { id: 1, name: 'Item 1' },
  { id: 2, name: 'Item 2' },
];
undefined

Active File Patterns

激活文件规则

Mark as active when:
  • File contains the primary teaching concept
  • Learner should focus on this code first
  • Component demonstrates the hook/pattern being taught
Effect of the
active
marker:
  • Sets initial editor tab focus when Sandpack loads
  • Signals "this is what you should study"
  • Works with hidden files to create focused examples
Most common active file:
src/index.js
or
src/App.js
Example:
mdx
```js src/App.js active
// This file will be focused when example loads
export default function App() {
  // ...
}
undefined
标记为active的场景:
  • 文件包含核心教学概念
  • 学习者应首先关注此代码
  • 组件演示了所教授的Hook/模式
active
标记的作用:
  • Sandpack加载时默认聚焦该编辑器标签
  • 提示“这是你需要重点学习的内容”
  • 与隐藏文件配合实现聚焦式示例
最常用的激活文件:
src/index.js
src/App.js
示例:
mdx
```js src/App.js active
// 示例加载时将聚焦此文件
export default function App() {
  // ...
}
undefined

File Structure Guidelines

文件结构指南

ScenarioStructureReason
Basic hook usageSingle fileSimple, focused
Teaching imports2-3 filesShows modularity
Context patterns4-5 filesRealistic structure
Complex state3+ filesSeparation of concerns
Single File Examples (70% of cases):
  • Use for simple concepts
  • 50-200 lines typical
  • Best for: Counter, text inputs, basic hooks
Multi-File Examples (30% of cases):
  • Use when teaching modularity/imports
  • Use for context patterns (4-5 files)
  • Use when component is reused
File Naming:
  • Main component:
    App.js
    (capitalized)
  • Component files:
    Gallery.js
    ,
    Button.js
    (capitalized)
  • Data files:
    data.js
    (lowercase)
  • Utility files:
    utils.js
    (lowercase)
  • Context files:
    TasksContext.js
    (named after what they provide)
场景结构原因
基础Hook用法单文件简洁、聚焦
教学导入语法2-3个文件展示模块化特性
Context模式4-5个文件贴近真实项目结构
复杂状态管理3个以上文件关注点分离
单文件示例(70%的场景):
  • 用于简单概念教学
  • 通常50-200行代码
  • 最适合:计数器、文本输入框、基础Hook
多文件示例(30%的场景):
  • 用于教授模块化/导入语法时
  • 用于Context模式教学(4-5个文件)
  • 组件需要复用的场景
文件命名:
  • 主组件:
    App.js
    (首字母大写)
  • 组件文件:
    Gallery.js
    ,
    Button.js
    (首字母大写)
  • 数据文件:
    data.js
    (小写)
  • 工具文件:
    utils.js
    (小写)
  • Context文件:
    TasksContext.js
    (根据提供的内容命名)

Code Size Limits

代码长度限制

  • Single file: <200 lines
  • Multi-file total: 150-300 lines
  • Main component: 100-150 lines
  • Supporting files: 20-40 lines each
  • 单文件:<200行
  • 多文件总长度:150-300行
  • 主组件:100-150行
  • 辅助文件:20-40行/个

CSS Guidelines

CSS 指南

Always:
  • Include minimal CSS for demo interactivity
  • Use semantic class names (
    .panel
    ,
    .button-primary
    ,
    .panel-dark
    )
  • Support light/dark themes when showing UI concepts
  • Keep CSS visible (never hidden)
Size Guidelines:
  • Minimal (5-10 lines): Basic button styling, spacing
  • Medium (15-30 lines): Panel styling, form layouts
  • Complex (40+ lines): Only for layout-focused examples
必须遵循:
  • 仅包含示例交互所需的最小CSS
  • 使用语义化类名(
    .panel
    ,
    .button-primary
    ,
    .panel-dark
  • 展示UI概念时支持明暗主题
  • 保持CSS可见(绝不隐藏)
长度指南:
  • 极简(5-10行):基础按钮样式、间距
  • 中等(15-30行):面板样式、表单布局
  • 复杂(40+行):仅用于布局相关示例