tamagui-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
This 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 TypeRequired ReadingCross-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
在使用这些组件时,请在编写代码前阅读对应的模式文档:
组件类型必读文档关联技能
Dialog、Sheet、模态浮层@DIALOG_PATTERNS.md
Form、Input、Label、校验@FORM_PATTERNS.md
typescript-best-practices
(zod)
动画、过渡效果@ANIMATION_PATTERNS.md
Popover、Tooltip、Select@OVERLAY_PATTERNS.md
编译器优化@COMPILER_PATTERNS.md
设计令牌、主题定制@DESIGN_SYSTEM.md

Config v4 Quick Start

Config v4 快速开始

Use
@tamagui/config/v4
for simplified setup:
tsx
// 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/v4
简化配置:
tsx
// 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
createThemes
with palette/accent/childrenThemes:
tsx
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使用
createThemes
tsx
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
    tokens.size[name]
    pattern
  • 属性:引用令牌时使用
    $
    前缀:
    <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
as const
on variants object until TypeScript supports inferred const generics.
特殊扩展运算符可将令牌类别映射到变体值:
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 const

Compound 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:
context
pattern does not work with compiler flattening. Use for higher-level components (Button, Card), not primitives (Stack, Text).
对于类似
<Button><Button.Text>Click</Button.Text></Button>
的复合API:
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>
注意
context
模式不适用于编译器扁平化。请将其用于高级组件(如Button、Card),而非基础组件(如Stack、Text)。

styleable() for Wrapper Components

用于包装组件的styleable()

When wrapping a styled component in a functional component, use
.styleable()
to preserve variant inheritance:
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
as const
on the accept object.
在非标准属性上启用令牌/主题解析:
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 const

Prop Order Matters

属性顺序很重要

In
styled()
, prop order determines override priority:
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 consistent
tsx
// 这些是不同的包,内容不同:
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
undefined

Core docs

核心文档

Component docs

组件文档

Full docs index

完整文档索引


For HTML pages, use the web-fetch skill with appropriate selectors.

如需获取HTML页面,请使用web-fetch技能并指定合适的选择器。

Quick Reference

快速参考

Config v4 Shorthands (Tailwind-aligned)

Config v4 简写(对齐Tailwind)

ShorthandProperty
bg
backgroundColor
p
padding
m
margin
w
width
h
height
br
borderRadius
简写属性
bg
backgroundColor
p
padding
m
margin
w
width
h
height
br
borderRadius

Media Query Breakpoints

媒体查询断点

TokenDefaultServer Default
$xs
660pxtrue
$sm
800pxfalse
$md
1020pxfalse
$lg
1280pxfalse
$xl
1420pxfalse
令牌默认值服务端默认值
$xs
660pxtrue
$sm
800pxfalse
$md
1020pxfalse
$lg
1280pxfalse
$xl
1420pxfalse

Animation Drivers

动画驱动

DriverPlatformUse Case
css
WebDefault, best performance
react-native-reanimated
NativeRequired for native animations
驱动平台使用场景
css
Web默认选项,性能最佳
react-native-reanimated
原生原生动画必需

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 - 设计令牌与主题定制