tamagui-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseThis skill provides patterns for Tamagui v1.x that go beyond fundamentals. It focuses on Config v4, compiler optimization, compound components, and common mistakes.
本技能提供Tamagui v1.x的进阶使用模式,重点覆盖Config v4、编译器优化、复合组件以及常见错误。
Mandatory Context Loading
必须加载的上下文内容
When working with these components, read the corresponding pattern file BEFORE writing code:
| Component Type | Required Reading | Cross-Skills |
|---|---|---|
| Dialog, Sheet, modal overlays | @DIALOG_PATTERNS.md | |
| Form, Input, Label, validation | @FORM_PATTERNS.md | |
| Animations, transitions | @ANIMATION_PATTERNS.md | |
| Popover, Tooltip, Select | @OVERLAY_PATTERNS.md | |
| Compiler optimization | @COMPILER_PATTERNS.md | |
| Design tokens, theming | @DESIGN_SYSTEM.md |
在使用这些组件时,请在编写代码前阅读对应的模式文档:
| 组件类型 | 必读文档 | 关联技能 |
|---|---|---|
| Dialog、Sheet、模态浮层 | @DIALOG_PATTERNS.md | |
| Form、Input、Label、校验 | @FORM_PATTERNS.md | |
| 动画、过渡效果 | @ANIMATION_PATTERNS.md | |
| Popover、Tooltip、Select | @OVERLAY_PATTERNS.md | |
| 编译器优化 | @COMPILER_PATTERNS.md | |
| 设计令牌、主题定制 | @DESIGN_SYSTEM.md |
Config v4 Quick Start
Config v4 快速开始
Use for simplified setup:
@tamagui/config/v4tsx
// tamagui.config.ts
import { defaultConfig } from '@tamagui/config/v4'
import { createTamagui } from 'tamagui'
export const config = createTamagui(defaultConfig)
type CustomConfig = typeof config
declare module 'tamagui' {
interface TamaguiCustomConfig extends CustomConfig {}
}Recommended setting for new projects (aligns flexBasis to React Native):
tsx
export const config = createTamagui({
...defaultConfig,
settings: {
...defaultConfig.settings,
styleCompat: 'react-native',
},
})使用 简化配置:
@tamagui/config/v4tsx
// tamagui.config.ts
import { defaultConfig } from '@tamagui/config/v4'
import { createTamagui } from 'tamagui'
export const config = createTamagui(defaultConfig)
type CustomConfig = typeof config
declare module 'tamagui' {
interface TamaguiCustomConfig extends CustomConfig {}
}新项目推荐配置(使flexBasis与React Native对齐):
tsx
export const config = createTamagui({
...defaultConfig,
settings: {
...defaultConfig.settings,
styleCompat: 'react-native',
},
})createThemes Pattern
createThemes 模式
For custom themes, use with palette/accent/childrenThemes:
createThemestsx
import { createThemes, defaultComponentThemes } from '@tamagui/config/v4'
const generatedThemes = createThemes({
componentThemes: defaultComponentThemes,
base: {
palette: {
dark: ['#050505', '#151515', /* ...12 colors */ '#fff'],
light: ['#fff', '#f8f8f8', /* ...12 colors */ '#000'],
},
extra: {
light: { ...Colors.blue, shadowColor: 'rgba(0,0,0,0.04)' },
dark: { ...Colors.blueDark, shadowColor: 'rgba(0,0,0,0.2)' },
},
},
accent: {
palette: { dark: lightPalette, light: darkPalette }, // inverted
},
childrenThemes: {
blue: { palette: { dark: Object.values(Colors.blueDark), light: Object.values(Colors.blue) } },
red: { /* ... */ },
green: { /* ... */ },
},
})如需自定义主题,结合palette/accent/childrenThemes使用:
createThemestsx
import { createThemes, defaultComponentThemes } from '@tamagui/config/v4'
const generatedThemes = createThemes({
componentThemes: defaultComponentThemes,
base: {
palette: {
dark: ['#050505', '#151515', /* ...12 colors */ '#fff'],
light: ['#fff', '#f8f8f8', /* ...12 colors */ '#000'],
},
extra: {
light: { ...Colors.blue, shadowColor: 'rgba(0,0,0,0.04)' },
dark: { ...Colors.blueDark, shadowColor: 'rgba(0,0,0,0.2)' },
},
},
accent: {
palette: { dark: lightPalette, light: darkPalette }, // 反转配色
},
childrenThemes: {
blue: { palette: { dark: Object.values(Colors.blueDark), light: Object.values(Colors.blue) } },
red: { /* ... */ },
green: { /* ... */ },
},
})Token and Theme Syntax
令牌与主题语法
$ Prefix Rules
$ 前缀规则
- Props: Use prefix for token references:
$<Text color="$color" fontSize="$4" /> - Theme keys: Access without in theme definitions:
${ color: palette[11] } - Token access in variants: Use pattern
tokens.size[name]
- 属性:引用令牌时使用前缀:
$<Text color="$color" fontSize="$4" /> - 主题键:在主题定义中无需前缀:
${ color: palette[11] } - 变体中的令牌访问:使用模式
tokens.size[name]
Variant Spread Operators
变体扩展运算符
Special spread operators map token categories to variant values:
tsx
const Button = styled(View, {
variants: {
size: {
// Maps size tokens: $1, $2, $true, etc.
'...size': (size, { tokens }) => ({
height: tokens.size[size] ?? size,
borderRadius: tokens.radius[size] ?? size,
gap: tokens.space[size]?.val * 0.2,
}),
},
textSize: {
// Maps fontSize tokens
'...fontSize': (name, { font }) => ({
fontSize: font?.size[name],
}),
},
} as const,
})Important: Use on variants object until TypeScript supports inferred const generics.
as const特殊扩展运算符可将令牌类别映射到变体值:
tsx
const Button = styled(View, {
variants: {
size: {
// 映射尺寸令牌:$1, $2, $true等
'...size': (size, { tokens }) => ({
height: tokens.size[size] ?? size,
borderRadius: tokens.radius[size] ?? size,
gap: tokens.space[size]?.val * 0.2,
}),
},
textSize: {
// 映射字体尺寸令牌
'...fontSize': (name, { font }) => ({
fontSize: font?.size[name],
}),
},
} as const,
})重要提示:在TypeScript支持推断const泛型之前,请在变体对象上使用。
as constCompound Components with createStyledContext
基于createStyledContext的复合组件
For compound APIs like :
<Button><Button.Text>Click</Button.Text></Button>tsx
import {
SizeTokens,
View,
Text,
createStyledContext,
styled,
withStaticProperties,
} from '@tamagui/core'
// 1. Create context with shared variant types
export const ButtonContext = createStyledContext<{ size: SizeTokens }>({
size: '$medium',
})
// 2. Create frame with context
export const ButtonFrame = styled(View, {
name: 'Button',
context: ButtonContext,
variants: {
size: {
'...size': (name, { tokens }) => ({
height: tokens.size[name],
borderRadius: tokens.radius[name],
gap: tokens.space[name].val * 0.2,
}),
},
} as const,
defaultVariants: {
size: '$medium',
},
})
// 3. Create text with same context (variants auto-sync)
export const ButtonText = styled(Text, {
name: 'ButtonText',
context: ButtonContext,
variants: {
size: {
'...fontSize': (name, { font }) => ({
fontSize: font?.size[name],
}),
},
} as const,
})
// 4. Compose with withStaticProperties
export const Button = withStaticProperties(ButtonFrame, {
Props: ButtonContext.Provider,
Text: ButtonText,
})Usage:
tsx
<Button size="$large">
<Button.Text>Click me</Button.Text>
</Button>
// Or override defaults from above:
<Button.Props size="$small">
<Button><Button.Text>Small</Button.Text></Button>
</Button.Props>Note: pattern does not work with compiler flattening. Use for higher-level components (Button, Card), not primitives (Stack, Text).
context对于类似的复合API:
<Button><Button.Text>Click</Button.Text></Button>tsx
import {
SizeTokens,
View,
Text,
createStyledContext,
styled,
withStaticProperties,
} from '@tamagui/core'
// 1. 创建包含共享变体类型的上下文
export const ButtonContext = createStyledContext<{ size: SizeTokens }>({
size: '$medium',
})
// 2. 创建带上下文的框架组件
export const ButtonFrame = styled(View, {
name: 'Button',
context: ButtonContext,
variants: {
size: {
'...size': (name, { tokens }) => ({
height: tokens.size[name],
borderRadius: tokens.radius[name],
gap: tokens.space[name].val * 0.2,
}),
},
} as const,
defaultVariants: {
size: '$medium',
},
})
// 3. 创建使用相同上下文的文本组件(变体自动同步)
export const ButtonText = styled(Text, {
name: 'ButtonText',
context: ButtonContext,
variants: {
size: {
'...fontSize': (name, { font }) => ({
fontSize: font?.size[name],
}),
},
} as const,
})
// 4. 使用withStaticProperties组合组件
export const Button = withStaticProperties(ButtonFrame, {
Props: ButtonContext.Provider,
Text: ButtonText,
})使用示例:
tsx
<Button size="$large">
<Button.Text>Click me</Button.Text>
</Button>
// 或覆盖上面的默认值:
<Button.Props size="$small">
<Button><Button.Text>Small</Button.Text></Button>
</Button.Props>注意:模式不适用于编译器扁平化。请将其用于高级组件(如Button、Card),而非基础组件(如Stack、Text)。
contextstyleable() for Wrapper Components
用于包装组件的styleable()
When wrapping a styled component in a functional component, use to preserve variant inheritance:
.styleable()tsx
const StyledText = styled(Text)
// WITHOUT styleable - BROKEN variant inheritance
const BrokenWrapper = (props) => <StyledText {...props} />
// WITH styleable - CORRECT
const CorrectWrapper = StyledText.styleable((props, ref) => (
<StyledText ref={ref} {...props} />
))
// Now this works:
const StyledCorrectWrapper = styled(CorrectWrapper, {
variants: {
bold: { true: { fontWeight: 'bold' } },
},
})在函数组件中包装样式化组件时,请使用来保留变体继承:
.styleable()tsx
const StyledText = styled(Text)
// 不使用styleable - 变体继承失效
const BrokenWrapper = (props) => <StyledText {...props} />
// 使用styleable - 正确实现
const CorrectWrapper = StyledText.styleable((props, ref) => (
<StyledText ref={ref} {...props} />
))
// 现在以下代码可以正常工作:
const StyledCorrectWrapper = styled(CorrectWrapper, {
variants: {
bold: { true: { fontWeight: 'bold' } },
},
})Adding Extra Props
添加额外属性
Pass generic type argument for additional props:
tsx
type ExtraProps = { icon?: React.ReactNode }
const IconText = StyledText.styleable<ExtraProps>((props, ref) => {
const { icon, ...rest } = props
return (
<XStack>
{icon}
<StyledText ref={ref} {...rest} />
</XStack>
)
})传递泛型类型参数以支持额外属性:
tsx
type ExtraProps = { icon?: React.ReactNode }
const IconText = StyledText.styleable<ExtraProps>((props, ref) => {
const { icon, ...rest } = props
return (
<XStack>
{icon}
<StyledText ref={ref} {...rest} />
</XStack>
)
})accept Prop for Custom Components
自定义组件的accept属性
Enable token/theme resolution on non-standard props:
tsx
// For SVG fill/stroke that should accept theme colors
const StyledSVG = styled(SVG, {}, {
accept: { fill: 'color', stroke: 'color' } as const,
})
// Usage: <StyledSVG fill="$blue10" />
// For style objects (like ScrollView's contentContainerStyle)
const MyScrollView = styled(ScrollView, {}, {
accept: { contentContainerStyle: 'style' } as const,
})
// Usage: <MyScrollView contentContainerStyle={{ padding: '$4' }} />Important: Use on the accept object.
as const在非标准属性上启用令牌/主题解析:
tsx
// 使SVG的fill/stroke属性支持主题颜色
const StyledSVG = styled(SVG, {}, {
accept: { fill: 'color', stroke: 'color' } as const,
})
// 使用示例: <StyledSVG fill="$blue10" />
// 针对样式对象(如ScrollView的contentContainerStyle)
const MyScrollView = styled(ScrollView, {}, {
accept: { contentContainerStyle: 'style' } as const,
})
// 使用示例: <MyScrollView contentContainerStyle={{ padding: '$4' }} />重要提示:在accept对象上使用。
as constProp Order Matters
属性顺序很重要
In , prop order determines override priority:
styled()tsx
// backgroundColor can be overridden by props
const Overridable = (props) => (
<View backgroundColor="$red10" {...props} width={200} />
)
// width CANNOT be overridden (comes after spread)
// Variant order matters too:
<Component scale={3} huge /> // scale = 3 (scale comes first)
<Component huge scale={3} /> // scale = 2 (huge overrides)在中,属性顺序决定了覆盖优先级:
styled()tsx
// backgroundColor可被props覆盖
const Overridable = (props) => (
<View backgroundColor="$red10" {...props} width={200} />
)
// width无法被覆盖(位于展开运算符之后)
// 变体顺序同样重要:
<Component scale={3} huge /> // scale = 3(scale在前)
<Component huge scale={3} /> // scale = 2(huge覆盖了scale)Anti-Patterns
反模式
Dynamic Styles Break Optimization
动态样式会破坏优化
tsx
// BAD - breaks compiler optimization
<View style={{ width: someVariable * 2 }} />
<View backgroundColor={isDark ? '$gray1' : '$gray12'} />
// GOOD - use variants
const Box = styled(View, {
variants: {
dark: { true: { backgroundColor: '$gray1' }, false: { backgroundColor: '$gray12' } },
},
})
<Box dark={isDark} />tsx
// 错误写法 - 破坏编译器优化
<View style={{ width: someVariable * 2 }} />
<View backgroundColor={isDark ? '$gray1' : '$gray12'} />
// 正确写法 - 使用变体
const Box = styled(View, {
variants: {
dark: { true: { backgroundColor: '$gray1' }, false: { backgroundColor: '$gray12' } },
},
})
<Box dark={isDark} />Inline Functions
内联函数
tsx
// BAD - new function every render
<View onPress={() => handlePress(id)} />
// GOOD - stable reference
const handlePressCallback = useCallback(() => handlePress(id), [id])
<View onPress={handlePressCallback} />tsx
// 错误写法 - 每次渲染都会创建新函数
<View onPress={() => handlePress(id)} />
// 正确写法 - 稳定引用
const handlePressCallback = useCallback(() => handlePress(id), [id])
<View onPress={handlePressCallback} />Wrong Import Paths
错误的导入路径
tsx
// These are different packages with different contents:
import { View } from 'tamagui' // Full UI kit
import { View } from '@tamagui/core' // Core only (smaller)
import { Button } from '@tamagui/button' // Individual component
// Pick one approach and be consistenttsx
// 这些是不同的包,内容不同:
import { View } from 'tamagui' // 完整UI套件
import { View } from '@tamagui/core' // 仅核心包(体积更小)
import { Button } from '@tamagui/button' // 独立组件包
// 选择一种方式并保持一致Mixing RN StyleSheet with Tamagui
混合使用RN StyleSheet与Tamagui
tsx
// BAD - StyleSheet values don't resolve tokens
const styles = StyleSheet.create({ box: { padding: 20 } })
<View style={styles.box} backgroundColor="$blue10" />
// GOOD - all Tamagui
<View padding="$4" backgroundColor="$blue10" />tsx
// 错误写法 - StyleSheet的值无法解析令牌
const styles = StyleSheet.create({ box: { padding: 20 } })
<View style={styles.box} backgroundColor="$blue10" />
// 正确写法 - 完全使用Tamagui
<View padding="$4" backgroundColor="$blue10" />Platform.OS Branching for Dialog/Sheet
针对Dialog/Sheet的Platform.OS分支判断
tsx
// BAD - manual platform branching
if (Platform.OS === 'web') {
return <Dialog>...</Dialog>
}
return <Sheet>...</Sheet>
// GOOD - use Adapt (see @DIALOG_PATTERNS.md)
<Dialog>
<Dialog.Portal>...</Dialog.Portal>
<Adapt when="sm" platform="touch">
<Sheet><Adapt.Contents /></Sheet>
</Adapt>
</Dialog>tsx
// 错误写法 - 手动进行平台分支判断
if (Platform.OS === 'web') {
return <Dialog>...</Dialog>
}
return <Sheet>...</Sheet>
// 正确写法 - 使用Adapt(详见@DIALOG_PATTERNS.md)
<Dialog>
<Dialog.Portal>...</Dialog.Portal>
<Adapt when="sm" platform="touch">
<Sheet><Adapt.Contents /></Sheet>
</Adapt>
</Dialog>Fetching Current Documentation
获取最新文档
For latest API details, fetch markdown docs directly:
bash
undefined如需获取最新API详情,可直接获取markdown文档:
bash
undefinedCore docs
核心文档
curl -sL "https://tamagui.dev/docs/core/configuration.md"
curl -sL "https://tamagui.dev/docs/core/styled.md"
curl -sL "https://tamagui.dev/docs/core/variants.md"
curl -sL "https://tamagui.dev/docs/core/animations.md"
curl -sL "https://tamagui.dev/docs/core/configuration.md"
curl -sL "https://tamagui.dev/docs/core/styled.md"
curl -sL "https://tamagui.dev/docs/core/variants.md"
curl -sL "https://tamagui.dev/docs/core/animations.md"
Component docs
组件文档
curl -sL "https://tamagui.dev/ui/sheet.md"
curl -sL "https://tamagui.dev/ui/dialog.md"
curl -sL "https://tamagui.dev/ui/select.md"
curl -sL "https://tamagui.dev/ui/sheet.md"
curl -sL "https://tamagui.dev/ui/dialog.md"
curl -sL "https://tamagui.dev/ui/select.md"
Full docs index
完整文档索引
curl -sL "https://tamagui.dev/llms.txt"
For HTML pages, use the web-fetch skill with appropriate selectors.curl -sL "https://tamagui.dev/llms.txt"
如需获取HTML页面,请使用web-fetch技能并指定合适的选择器。Quick Reference
快速参考
Config v4 Shorthands (Tailwind-aligned)
Config v4 简写(对齐Tailwind)
| Shorthand | Property |
|---|---|
| backgroundColor |
| padding |
| margin |
| width |
| height |
| borderRadius |
| 简写 | 属性 |
|---|---|
| backgroundColor |
| padding |
| margin |
| width |
| height |
| borderRadius |
Media Query Breakpoints
媒体查询断点
| Token | Default | Server Default |
|---|---|---|
| 660px | true |
| 800px | false |
| 1020px | false |
| 1280px | false |
| 1420px | false |
| 令牌 | 默认值 | 服务端默认值 |
|---|---|---|
| 660px | true |
| 800px | false |
| 1020px | false |
| 1280px | false |
| 1420px | false |
Animation Drivers
动画驱动
| Driver | Platform | Use Case |
|---|---|---|
| Web | Default, best performance |
| Native | Required for native animations |
| 驱动 | 平台 | 使用场景 |
|---|---|---|
| Web | 默认选项,性能最佳 |
| 原生 | 原生动画必需 |
Additional Pattern Files
其他模式文档
- @DIALOG_PATTERNS.md - Dialog, Sheet, Adapt, accessibility
- @FORM_PATTERNS.md - Form, Input, Label, validation with zod
- @ANIMATION_PATTERNS.md - Animation drivers, enterStyle/exitStyle
- @OVERLAY_PATTERNS.md - Popover, Tooltip, Select
- @COMPILER_PATTERNS.md - Compiler optimization details
- @DESIGN_SYSTEM.md - Design tokens and theming
- @DIALOG_PATTERNS.md - Dialog、Sheet、Adapt、无障碍访问
- @FORM_PATTERNS.md - Form、Input、Label、zod校验
- @ANIMATION_PATTERNS.md - 动画驱动、enterStyle/exitStyle
- @OVERLAY_PATTERNS.md - Popover、Tooltip、Select
- @COMPILER_PATTERNS.md - 编译器优化详情
- @DESIGN_SYSTEM.md - 设计令牌与主题定制