tamagui-best-practices by 0xbigboss/claude-code
npx skills add https://github.com/0xbigboss/claude-code --skill tamagui-best-practices此技能提供超越基础知识的 Tamagui v1.x 模式。它侧重于配置 v4、编译器优化、复合组件和常见错误。
使用这些组件时,请在编写代码前阅读相应的模式文件:
| 组件类型 | 必读内容 | 跨技能 |
|---|---|---|
| 对话框、表单、模态覆盖层 | @DIALOG_PATTERNS.md | |
| 表单、输入框、标签、验证 | @FORM_PATTERNS.md | typescript-best-practices (zod) |
| 动画、过渡效果 | @ANIMATION_PATTERNS.md | |
| 弹出框、工具提示、选择器 | @OVERLAY_PATTERNS.md | |
| 编译器优化 | @COMPILER_PATTERNS.md | |
| 设计令牌、主题系统 | @DESIGN_SYSTEM.md |
使用 @tamagui/config/v4 进行简化设置:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
// 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 对齐):
export const config = createTamagui({
...defaultConfig,
settings: {
...defaultConfig.settings,
styleCompat: 'react-native',
},
})
对于自定义主题,使用带有 palette/accent/childrenThemes 的 createThemes:
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: { /* ... */ },
},
})
$ 前缀引用令牌:<Text color="$color" fontSize="$4" />$:{ color: palette[11] }tokens.size[name] 模式特殊的展开运算符将令牌类别映射到变体值:
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,
})
重要:在变体对象上使用 as const,直到 TypeScript 支持推断的常量泛型。
对于像 <Button><Button.Text>点击</Button.Text></Button> 这样的复合 API:
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,
})
用法:
<Button size="$large">
<Button.Text>点击我</Button.Text>
</Button>
// 或者从上方覆盖默认值:
<Button.Props size="$small">
<Button><Button.Text>小号</Button.Text></Button>
</Button.Props>
注意:context 模式不适用于编译器扁平化。用于高级组件(按钮、卡片),而非原始组件(堆栈、文本)。
在函数组件中包装样式化组件时,使用 .styleable() 来保留变体继承:
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' } },
},
})
传递泛型类型参数以添加额外属性:
type ExtraProps = { icon?: React.ReactNode }
const IconText = StyledText.styleable<ExtraProps>((props, ref) => {
const { icon, ...rest } = props
return (
<XStack>
{icon}
<StyledText ref={ref} {...rest} />
</XStack>
)
})
在非标准属性上启用令牌/主题解析:
// 对于应接受主题颜色的 SVG 填充/描边
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 const。
在 styled() 中,属性顺序决定了覆盖优先级:
// backgroundColor 可以被属性覆盖
const Overridable = (props) => (
<View backgroundColor="$red10" {...props} width={200} />
)
// width 无法被覆盖(在展开运算符之后)
// 变体顺序也很重要:
<Component scale={3} huge /> // scale = 3(scale 在前)
<Component huge scale={3} /> // scale = 2(huge 覆盖)
// 错误 - 破坏编译器优化
<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} />
// 错误 - 每次渲染都创建新函数
<View onPress={() => handlePress(id)} />
// 正确 - 稳定引用
const handlePressCallback = useCallback(() => handlePress(id), [id])
<View onPress={handlePressCallback} />
// 这些是具有不同内容的不同包:
import { View } from 'tamagui' // 完整 UI 套件
import { View } from '@tamagui/core' // 仅核心(更小)
import { Button } from '@tamagui/button' // 单个组件
// 选择一种方法并保持一致
// 错误 - StyleSheet 值不解析令牌
const styles = StyleSheet.create({ box: { padding: 20 } })
<View style={styles.box} backgroundColor="$blue10" />
// 正确 - 全部使用 Tamagui
<View padding="$4" backgroundColor="$blue10" />
// 错误 - 手动平台分支
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>
获取最新的 API 详情,直接获取 markdown 文档:
# 核心文档
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/ui/sheet.md"
curl -sL "https://tamagui.dev/ui/dialog.md"
curl -sL "https://tamagui.dev/ui/select.md"
# 完整文档索引
curl -sL "https://tamagui.dev/llms.txt"
对于 HTML 页面,请使用 web-fetch 技能并配合适当的选择器。
| 简写 | 属性 |
|---|---|
bg | backgroundColor |
p | padding |
m | margin |
w | width |
h | height |
br | borderRadius |
| 令牌 | 默认值 | 服务器默认值 |
|---|---|---|
$xs | 660px | true |
$sm | 800px | false |
$md | 1020px | false |
$lg | 1280px | false |
$xl | 1420px | false |
| 驱动 | 平台 | 使用场景 |
|---|---|---|
css | Web | 默认,最佳性能 |
react-native-reanimated | Native | 原生动画必需 |
每周安装次数
165
仓库
GitHub 星标数
37
首次出现
2026年1月20日
安全审计
安装于
gemini-cli126
claude-code124
opencode123
codex121
github-copilot117
cursor110
This skill provides patterns for Tamagui v1.x that go beyond fundamentals. It focuses on Config v4, compiler optimization, compound components, and common mistakes.
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 | typescript-best-practices (zod) |
| Animations, transitions | @ANIMATION_PATTERNS.md | |
| Popover, Tooltip, Select | @OVERLAY_PATTERNS.md | |
| Compiler optimization | @COMPILER_PATTERNS.md | |
| Design tokens, theming | @DESIGN_SYSTEM.md |
Use @tamagui/config/v4 for simplified setup:
// 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):
export const config = createTamagui({
...defaultConfig,
settings: {
...defaultConfig.settings,
styleCompat: 'react-native',
},
})
For custom themes, use createThemes with palette/accent/childrenThemes:
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: { /* ... */ },
},
})
$ prefix for token references: <Text color="$color" fontSize="$4" />$ in theme definitions: { color: palette[11] }tokens.size[name] patternSpecial spread operators map token categories to variant values:
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 as const on variants object until TypeScript supports inferred const generics.
For compound APIs like <Button><Button.Text>Click</Button.Text></Button>:
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 :
<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 : context pattern does not work with compiler flattening. Use for higher-level components (Button, Card), not primitives (Stack, Text).
When wrapping a styled component in a functional component, use .styleable() to preserve variant inheritance:
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' } },
},
})
Pass generic type argument for additional props:
type ExtraProps = { icon?: React.ReactNode }
const IconText = StyledText.styleable<ExtraProps>((props, ref) => {
const { icon, ...rest } = props
return (
<XStack>
{icon}
<StyledText ref={ref} {...rest} />
</XStack>
)
})
Enable token/theme resolution on non-standard props:
// 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 as const on the accept object.
In styled(), prop order determines override priority:
// 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)
// 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} />
// BAD - new function every render
<View onPress={() => handlePress(id)} />
// GOOD - stable reference
const handlePressCallback = useCallback(() => handlePress(id), [id])
<View onPress={handlePressCallback} />
// 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 consistent
// 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" />
// 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>
For latest API details, fetch markdown docs directly:
# Core 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"
# 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"
# Full docs index
curl -sL "https://tamagui.dev/llms.txt"
For HTML pages, use the web-fetch skill with appropriate selectors.
| Shorthand | Property |
|---|---|
bg | backgroundColor |
p | padding |
m | margin |
w | width |
h | height |
br | borderRadius |
| Token | Default | Server Default |
|---|---|---|
$xs | 660px | true |
$sm | 800px | false |
$md | 1020px | false |
$lg | 1280px | false |
$xl | 1420px | false |
| Driver | Platform | Use Case |
|---|---|---|
css | Web | Default, best performance |
react-native-reanimated | Native | Required for native animations |
Weekly Installs
165
Repository
GitHub Stars
37
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
gemini-cli126
claude-code124
opencode123
codex121
github-copilot117
cursor110
GSAP 框架集成指南:Vue、Svelte 等框架中 GSAP 动画最佳实践
3,200 周安装