tamagui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTamagui Skill
Tamagui 技能指南
Universal React UI framework for web and native with an optimizing compiler.
这是一款适用于Web和原生平台的通用React UI框架,配备优化编译器。
Getting Project-Specific Config
获取项目专属配置
Before writing Tamagui code, get the project's actual configuration:
bash
npx tamagui generate-promptThis outputs with the project's specific:
tamagui-prompt.md- Design tokens (space, size, radius, color, zIndex)
- Theme names and hierarchy
- Available components
- Media query breakpoints
- Shorthand properties
- Font families
Always reference this file for token/theme/media query names rather than guessing or using defaults.
在编写Tamagui代码前,请先获取项目的实际配置:
bash
npx tamagui generate-prompt该命令会输出文件,包含项目专属的以下内容:
tamagui-prompt.md- 设计令牌(间距、尺寸、圆角、颜色、层级zIndex)
- 主题名称与层级结构
- 可用组件
- 媒体查询断点
- 简写属性
- 字体族
请始终参考此文件中的令牌/主题/媒体查询名称,而非自行猜测或使用默认值。
Core Concepts
核心概念
styled() Function
styled() 函数
Create components by extending existing ones:
tsx
import { View, Text, styled } from '@tamagui/core'
const Card = styled(View, {
padding: '$4', // use tokens with $
backgroundColor: '$background',
borderRadius: '$4',
variants: {
size: {
small: { padding: '$2' },
large: { padding: '$6' },
},
elevated: {
true: {
shadowColor: '$shadowColor',
shadowRadius: 10,
},
},
} as const, // required for type inference
defaultVariants: {
size: 'small',
},
})
// usage
<Card size="large" elevated />Key rules:
- Always use on variants objects
as const - Tokens use prefix:
$,$4,$background$color11 - Prop order matters - later props override earlier ones
- Variants defined later in the object override earlier ones
通过扩展现有组件来创建新组件:
tsx
import { View, Text, styled } from '@tamagui/core'
const Card = styled(View, {
padding: '$4', // 使用$前缀调用令牌
backgroundColor: '$background',
borderRadius: '$4',
variants: {
size: {
small: { padding: '$2' },
large: { padding: '$6' },
},
elevated: {
true: {
shadowColor: '$shadowColor',
shadowRadius: 10,
},
},
} as const, // 必须添加以支持TypeScript类型推断
defaultVariants: {
size: 'small',
},
})
// 使用示例
<Card size="large" elevated />关键规则:
- 必须为variants对象添加
as const - 令牌使用$前缀:、
$4、$background$color11 - 属性顺序会影响优先级,后续属性会覆盖前面的属性
- 后定义的变体优先级高于先定义的变体
Stack Components
布局栈组件
tsx
import { XStack, YStack, ZStack } from 'tamagui'
// XStack = flexDirection: 'row'
// YStack = flexDirection: 'column'
// ZStack = position: 'relative' with absolute children
<YStack gap="$4" padding="$4">
<XStack justifyContent="space-between" alignItems="center">
<Text>Label</Text>
<Button>Action</Button>
</XStack>
</YStack>tsx
import { XStack, YStack, ZStack } from 'tamagui'
// XStack = flexDirection: 'row'
// YStack = flexDirection: 'column'
// ZStack = position: 'relative',子元素为绝对定位
<YStack gap="$4" padding="$4">
<XStack justifyContent="space-between" alignItems="center">
<Text>标签</Text>
<Button>操作</Button>
</XStack>
</YStack>Themes
主题系统
Themes nest and combine hierarchically:
tsx
import { Theme } from 'tamagui'
// base theme
<Theme name="dark">
{/* sub-theme */}
<Theme name="blue">
{/* uses dark_blue theme */}
<Button>Blue button on dark</Button>
</Theme>
</Theme>
// access theme values
const theme = useTheme()
console.log(theme.background.val) // actual color value
console.log(theme.color11.val) // high contrast text12-step color scale convention:
- : backgrounds (subtle to emphasized)
$color1-4 - : borders, separators
$color5-6 - : hover/active states
$color7-8 - : solid backgrounds
$color9-10 - : text (low to high contrast)
$color11-12
主题支持嵌套与层级组合:
tsx
import { Theme } from 'tamagui'
// 基础主题
<Theme name="dark">
{/* 子主题 */}
<Theme name="blue">
{/* 使用dark_blue主题 */}
<Button>深色背景下的蓝色按钮</Button>
</Theme>
</Theme>
// 获取主题值
const theme = useTheme()
console.log(theme.background.val) // 实际颜色值
console.log(theme.color11.val) // 高对比度文本12阶颜色体系约定:
- :背景色(从浅到深)
$color1-4 - :边框、分隔线
$color5-6 - :悬停/激活状态
$color7-8 - :实色背景
$color9-10 - :文本(从低对比度到高对比度)
$color11-12
Responsive Styles
响应式样式
Use media query props (check your for actual breakpoint names):
tamagui-prompt.mdtsx
<YStack
padding="$4"
$gtSm={{ padding: '$6' }} // check your config for actual names
$gtMd={{ padding: '$8' }}
flexDirection="column"
$gtLg={{ flexDirection: 'row' }}
/>
// or with hook
const media = useMedia()
if (media.gtMd) {
// render for medium+ screens
}使用媒体查询属性(请查看获取实际断点名称):
tamagui-prompt.mdtsx
<YStack
padding="$4"
$gtSm={{ padding: '$6' }} // 请根据你的配置使用实际名称
$gtMd={{ padding: '$8' }}
flexDirection="column"
$gtLg={{ flexDirection: 'row' }}
/>
// 或使用钩子
const media = useMedia()
if (media.gtMd) {
// 为中等及以上屏幕渲染内容
}Animations
动画实现
tsx
import { AnimatePresence } from 'tamagui'
<AnimatePresence>
{show && (
<YStack
key="modal" // key required for exit animations
animation="quick"
enterStyle={{ opacity: 0, y: -20 }}
exitStyle={{ opacity: 0, y: 20 }}
opacity={1}
y={0}
/>
)}
</AnimatePresence>Animation drivers:
- - web only, CSS transitions
@tamagui/animations-css - - native Animated API
@tamagui/animations-react-native - - best native performance
@tamagui/animations-reanimated - - spring physics
@tamagui/animations-motion
CSS driver uses easing strings, others support spring physics.
tsx
import { AnimatePresence } from 'tamagui'
<AnimatePresence>
{show && (
<YStack
key="modal" // 退出动画必须添加key
animation="quick"
enterStyle={{ opacity: 0, y: -20 }}
exitStyle={{ opacity: 0, y: 20 }}
opacity={1}
y={0}
/>
)}
</AnimatePresence>动画驱动:
- - 仅支持Web,基于CSS过渡
@tamagui/animations-css - - 基于原生Animated API
@tamagui/animations-react-native - - 原生平台性能最优
@tamagui/animations-reanimated - - 弹簧物理动画
@tamagui/animations-motion
CSS驱动使用缓动字符串,其他驱动支持弹簧物理效果。
Compound Components
复合组件
Use for components that share state:
createStyledContexttsx
import { createStyledContext, styled, View, Text } from '@tamagui/core'
import { withStaticProperties } from '@tamagui/helpers'
const CardContext = createStyledContext({ size: 'medium' as 'small' | 'medium' | 'large' })
const CardFrame = styled(View, {
context: CardContext,
padding: '$4',
backgroundColor: '$background',
variants: {
size: {
small: { padding: '$2' },
medium: { padding: '$4' },
large: { padding: '$6' },
},
} as const,
})
const CardTitle = styled(Text, {
context: CardContext, // inherits size from parent
fontWeight: 'bold',
variants: {
size: {
small: { fontSize: '$4' },
medium: { fontSize: '$5' },
large: { fontSize: '$6' },
},
} as const,
})
export const Card = withStaticProperties(CardFrame, {
Title: CardTitle,
})
// usage - size cascades to children
<Card size="large">
<Card.Title>Large Title</Card.Title>
</Card>使用创建共享状态的组件:
createStyledContexttsx
import { createStyledContext, styled, View, Text } from '@tamagui/core'
import { withStaticProperties } from '@tamagui/helpers'
const CardContext = createStyledContext({ size: 'medium' as 'small' | 'medium' | 'large' })
const CardFrame = styled(View, {
context: CardContext,
padding: '$4',
backgroundColor: '$background',
variants: {
size: {
small: { padding: '$2' },
medium: { padding: '$4' },
large: { padding: '$6' },
},
} as const,
})
const CardTitle = styled(Text, {
context: CardContext, // 继承父组件的size属性
fontWeight: 'bold',
variants: {
size: {
small: { fontSize: '$4' },
medium: { fontSize: '$5' },
large: { fontSize: '$6' },
},
} as const,
})
export const Card = withStaticProperties(CardFrame, {
Title: CardTitle,
})
// 使用示例 - size属性会传递给子组件
<Card size="large">
<Card.Title>大标题</Card.Title>
</Card>Common Patterns
常见模式
Dialog with Adapt (Sheet on Mobile)
自适应对话框(移动端为底部弹窗)
tsx
import { Dialog, Sheet, Adapt, Button } from 'tamagui'
<Dialog>
<Dialog.Trigger asChild>
<Button>Open</Button>
</Dialog.Trigger>
<Adapt when="sm" platform="touch">
<Sheet modal dismissOnSnapToBottom>
<Sheet.Frame padding="$4">
<Adapt.Contents />
</Sheet.Frame>
<Sheet.Overlay />
</Sheet>
</Adapt>
<Dialog.Portal>
<Dialog.Overlay
key="overlay"
animation="quick"
opacity={0.5}
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
/>
<Dialog.Content
key="content"
animation="quick"
enterStyle={{ opacity: 0, scale: 0.95 }}
exitStyle={{ opacity: 0, scale: 0.95 }}
>
<Dialog.Title>Title</Dialog.Title>
<Dialog.Description>Description</Dialog.Description>
<Dialog.Close asChild>
<Button>Close</Button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog>tsx
import { Dialog, Sheet, Adapt, Button } from 'tamagui'
<Dialog>
<Dialog.Trigger asChild>
<Button>打开</Button>
</Dialog.Trigger>
<Adapt when="sm" platform="touch">
<Sheet modal dismissOnSnapToBottom>
<Sheet.Frame padding="$4">
<Adapt.Contents />
</Sheet.Frame>
<Sheet.Overlay />
</Sheet>
</Adapt>
<Dialog.Portal>
<Dialog.Overlay
key="overlay"
animation="quick"
opacity={0.5}
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
/>
<Dialog.Content
key="content"
animation="quick"
enterStyle={{ opacity: 0, scale: 0.95 }}
exitStyle={{ opacity: 0, scale: 0.95 }}
>
<Dialog.Title>标题</Dialog.Title>
<Dialog.Description>描述</Dialog.Description>
<Dialog.Close asChild>
<Button>关闭</Button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog>Form with Input/Label
表单与输入框/标签
tsx
import { Input, Label, YStack, XStack, Button } from 'tamagui'
<YStack gap="$4" padding="$4">
<YStack gap="$2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
placeholder="email@example.com"
autoCapitalize="none"
keyboardType="email-address"
/>
</YStack>
<XStack gap="$2" justifyContent="flex-end">
<Button variant="outlined">Cancel</Button>
<Button theme="blue">Submit</Button>
</XStack>
</YStack>tsx
import { Input, Label, YStack, XStack, Button } from 'tamagui'
<YStack gap="$4" padding="$4">
<YStack gap="$2">
<Label htmlFor="email">邮箱</Label>
<Input
id="email"
placeholder="email@example.com"
autoCapitalize="none"
keyboardType="email-address"
/>
</YStack>
<XStack gap="$2" justifyContent="flex-end">
<Button variant="outlined">取消</Button>
<Button theme="blue">提交</Button>
</XStack>
</YStack>Anti-Patterns
反模式
❌ Hardcoded values instead of tokens
❌ 使用硬编码值而非令牌
tsx
// bad
<View padding={16} backgroundColor="#fff" />
// good - uses design tokens
<View padding="$4" backgroundColor="$background" />tsx
// 错误示例
<View padding={16} backgroundColor="#fff" />
// 正确示例 - 使用设计令牌
<View padding="$4" backgroundColor="$background" />❌ Missing as const
on variants
as const❌ 变体对象缺少as const
as consttsx
// bad - TypeScript can't infer variant types
variants: {
size: { small: {...}, large: {...} }
}
// good
variants: {
size: { small: {...}, large: {...} }
} as consttsx
// 错误示例 - TypeScript无法推断变体类型
variants: {
size: { small: {...}, large: {...} }
}
// 正确示例
variants: {
size: { small: {...}, large: {...} }
} as const❌ Platform detection in styled()
❌ 在styled()中检测平台
tsx
// bad - won't be extracted by compiler
const Box = styled(View, {
padding: Platform.OS === 'web' ? 10 : 20,
})
// good - use platform modifiers
const Box = styled(View, {
padding: 20,
'$platform-web': { padding: 10 },
})tsx
// 错误示例 - 编译器无法提取样式
const Box = styled(View, {
padding: Platform.OS === 'web' ? 10 : 20,
})
// 正确示例 - 使用平台修饰符
const Box = styled(View, {
padding: 20,
'$platform-web': { padding: 10 },
})❌ exitStyle without AnimatePresence
❌ 未使用AnimatePresence却添加exitStyle
tsx
// bad - exit animation won't work
{show && <View exitStyle={{ opacity: 0 }} />}
// good
<AnimatePresence>
{show && <View key="box" exitStyle={{ opacity: 0 }} />}
</AnimatePresence>tsx
// 错误示例 - 退出动画无法生效
{show && <View exitStyle={{ opacity: 0 }} />}
// 正确示例
<AnimatePresence>
{show && <View key="box" exitStyle={{ opacity: 0 }} />}
</AnimatePresence>❌ Dynamic values that prevent extraction
❌ 使用动态值导致无法提取样式
tsx
// bad - runtime variable prevents compiler extraction
const dynamicPadding = isPremium ? '$6' : '$4'
<View padding={dynamicPadding} />
// good - inline ternary is extractable
<View padding={isPremium ? '$6' : '$4'} />tsx
// 错误示例 - 运行时变量导致编译器无法提取样式
const dynamicPadding = isPremium ? '$6' : '$4'
<View padding={dynamicPadding} />
// 正确示例 - 内联三元表达式可被提取
<View padding={isPremium ? '$6' : '$4'} />❌ Wrong media query order
❌ 媒体查询顺序错误
tsx
// bad - base value overrides responsive
<View $gtMd={{ padding: '$8' }} padding="$4" />
// good - base first, then responsive overrides
<View padding="$4" $gtMd={{ padding: '$8' }} />tsx
// 错误示例 - 基础值覆盖响应式值
<View $gtMd={{ padding: '$8' }} padding="$4" />
// 正确示例 - 先设置基础值,再添加响应式覆盖
<View padding="$4" $gtMd={{ padding: '$8' }} />❌ Spring animations with CSS driver
❌ 在CSS驱动中使用弹簧动画
tsx
// bad - CSS driver doesn't support spring physics
import { createAnimations } from '@tamagui/animations-css'
const anims = createAnimations({
bouncy: { type: 'spring', damping: 10 } // won't work
})
// good for CSS driver - use easing strings
const anims = createAnimations({
bouncy: 'cubic-bezier(0.68, -0.55, 0.265, 1.55) 300ms'
})tsx
// 错误示例 - CSS驱动不支持弹簧物理
import { createAnimations } from '@tamagui/animations-css'
const anims = createAnimations({
bouncy: { type: 'spring', damping: 10 } // 无法生效
})
// 正确示例 - CSS驱动使用缓动字符串
const anims = createAnimations({
bouncy: 'cubic-bezier(0.68, -0.55, 0.265, 1.55) 300ms'
})Compiler Optimization
编译器优化
The Tamagui compiler extracts static styles to CSS at build time. For styles to be extracted:
- Use tokens - extracts,
$4may not16 - Inline ternaries - extracts
padding={x ? '$4' : '$2'} - Avoid runtime variables - computed values don't extract
- Use variants - better than conditional props
Check if extraction is working:
- Look for attributes in dev mode
data-tamagui - Bundle size should be smaller with compiler enabled
- Styles should appear as CSS classes, not inline
Tamagui编译器会在构建阶段将静态样式提取为CSS。要确保样式可被提取,请遵循以下规则:
- 使用令牌 - 可被提取,
$4可能无法被提取16 - 使用内联三元表达式 - 可被提取
padding={x ? '$4' : '$2'} - 避免运行时变量 - 计算值无法被提取
- 使用变体 - 比条件属性更优
检查样式是否被提取:
- 在开发模式下查看是否有属性
data-tamagui - 启用编译器后包体积应更小
- 样式应显示为CSS类,而非内联样式
TypeScript
TypeScript支持
tsx
import { GetProps, styled, View } from '@tamagui/core'
const MyComponent = styled(View, {
variants: {
size: { small: {}, large: {} }
} as const,
})
// extract props type
type MyComponentProps = GetProps<typeof MyComponent>
// extend with custom props
interface ExtendedProps extends MyComponentProps {
onCustomEvent?: () => void
}tsx
import { GetProps, styled, View } from '@tamagui/core'
const MyComponent = styled(View, {
variants: {
size: { small: {}, large: {} }
} as const,
})
// 提取属性类型
type MyComponentProps = GetProps<typeof MyComponent>
// 扩展自定义属性
interface ExtendedProps extends MyComponentProps {
onCustomEvent?: () => void
}Quick Reference
速查手册
| Pattern | Example |
|---|---|
| Token | |
| Theme value | |
| Color scale | |
| Responsive | |
| Variant | |
| Animation | |
| Theme switch | |
| Compound | |
| 模式 | 示例 |
|---|---|
| 令牌 | |
| 主题值 | |
| 颜色体系 | |
| 响应式 | |
| 变体 | |
| 动画 | |
| 主题切换 | |
| 复合组件 | |
Resources
资源链接
- Docs: https://tamagui.dev
- GitHub: https://github.com/tamagui/tamagui
- Discord: https://discord.gg/tamagui
- 文档:https://tamagui.dev
- GitHub:https://github.com/tamagui/tamagui
- Discord:https://discord.gg/tamagui