tamagui-v2

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Tamagui v2 Skill

Tamagui v2 技能指南

Universal React UI framework for web and native (v1.144.0+)
This skill provides comprehensive guidance for building cross-platform React applications using Tamagui's styling system, component library, Bento premium components, and Takeout starter kit.

适用于Web和原生平台的通用React UI框架(v1.144.0+)
本技能指南提供了使用Tamagui的样式系统、组件库、Bento高级组件以及Takeout启动套件构建跨平台React应用的全面指导。

Overview

概述

Tamagui is a universal UI framework that lets you write once and deploy to both web and React Native with optimal performance. It features:
  • Unified styling API -
    styled()
    function with design tokens, variants, and responsive patterns
  • Optimizing compiler - Extracts styles to atomic CSS at build time for maximum performance
  • Theme system - 12-step color scales with automatic light/dark mode support
  • Cross-platform components - Button, Dialog, Sheet, Input, Select, Tabs, and more
  • Premium ecosystem - Bento (production forms, tables, lists) + Takeout (full-stack starter with routing, auth, database, real-time sync)
Version: 1.144.0+
Platforms: Web (React), iOS/Android (React Native), Expo
License: Open source (MIT) + Bento/Takeout (commercial licenses)

Tamagui是一款通用UI框架,支持一次编写即可部署到Web和React Native平台,同时保持最优性能。它具备以下特性:
  • 统一样式API - 带有设计令牌、变体和响应式模式的
    styled()
    函数
  • 优化编译器 - 在构建时将样式提取为原子化CSS,实现最高性能
  • 主题系统 - 包含12阶颜色刻度,自动支持明暗模式
  • 跨平台组件 - Button、Dialog、Sheet、Input、Select、Tabs等
  • 高级生态系统 - Bento(生产级表单、表格、列表)+ Takeout(包含路由、认证、数据库、实时同步的全栈启动套件)
版本: 1.144.0+
支持平台: Web(React)、iOS/Android(React Native)、Expo
授权协议: 开源(MIT)+ Bento/Takeout(商业授权)

When to Use This Skill

适用场景

Activate this skill when encountering these triggers:
当遇到以下触发词时,可启用本技能:

Core Framework

核心框架

  • "tamagui", "universal UI", "react native web", "cross-platform UI"
  • "styled()", "design tokens", "$token", "theme tokens"
  • "XStack", "YStack", "ZStack", "Stack components"
  • "useTheme", "useMedia", "responsive design"
  • "@tamagui/*" imports, "@tamagui/core"
  • "createStyledContext", "withStaticProperties"
  • "variant", "variants", "defaultVariants"
  • "tamagui"、"universal UI"、"react native web"、"cross-platform UI"
  • "styled()"、"design tokens"、"$token"、"theme tokens"
  • "XStack"、"YStack"、"ZStack"、"Stack components"
  • "useTheme"、"useMedia"、"responsive design"
  • "@tamagui/*" 导入、"@tamagui/core"
  • "createStyledContext"、"withStaticProperties"
  • "variant"、"variants"、"defaultVariants"

Bento Components

Bento组件

  • "bento", "@tamagui/bento", "bento components"
  • "React Hook Form", "react-hook-form", "form validation"
  • "Zod", "zod validation", "form schema"
  • "TanStack Table", "@tanstack/react-table", "data table"
  • "virtualized list", "FlatList", "masonry layout"
  • "bento"、"@tamagui/bento"、"bento components"
  • "React Hook Form"、"react-hook-form"、"form validation"
  • "Zod"、"zod validation"、"form schema"
  • "TanStack Table"、"@tanstack/react-table"、"data table"
  • "virtualized list"、"FlatList"、"masonry layout"

Takeout Starter

Takeout启动套件

  • "takeout", "tamagui takeout", "one.js"
  • "file-based routing", "one router", "SSG", "SSR"
  • "Better Auth", "authentication", "OAuth"
  • "Drizzle ORM", "database", "PostgreSQL"
  • "Zero Sync", "real-time sync", "offline-first"
  • "takeout"、"tamagui takeout"、"one.js"
  • "file-based routing"、"one router"、"SSG"、"SSR"
  • "Better Auth"、"authentication"、"OAuth"
  • "Drizzle ORM"、"database"、"PostgreSQL"
  • "Zero Sync"、"real-time sync"、"offline-first"

Platform-Specific

平台特定功能

  • "animation driver", "CSS animations", "Reanimated"
  • "compiler optimization", "static extraction", "bundle size"
  • "web-only", "native-only", "platform-specific"

  • "animation driver"、"CSS animations"、"Reanimated"
  • "compiler optimization"、"static extraction"、"bundle size"
  • "web-only"、"native-only"、"platform-specific"

Quick Start Patterns

快速入门示例

1. Basic Styling with styled()

1. 使用styled()进行基础样式设置

Create custom components by extending existing ones:
tsx
import { View, Text, styled } from '@tamagui/core'

// Simple styled component
export const Card = styled(View, {
  padding: '$4',
  backgroundColor: '$background',
  borderRadius: '$4',
  borderWidth: 1,
  borderColor: '$borderColor',
})

// With variants
export const Button = styled(View, {
  padding: '$3',
  borderRadius: '$2',
  backgroundColor: '$blue10',
  cursor: 'pointer',
  
  variants: {
    variant: {
      primary: {
        backgroundColor: '$blue10',
      },
      secondary: {
        backgroundColor: '$gray5',
      },
      outlined: {
        backgroundColor: 'transparent',
        borderWidth: 1,
        borderColor: '$borderColor',
      },
    },
    size: {
      small: { padding: '$2' },
      medium: { padding: '$3' },
      large: { padding: '$4' },
    },
  } as const,
  
  defaultVariants: {
    variant: 'primary',
    size: 'medium',
  },
})

// Usage
<Button variant="outlined" size="large">Click Me</Button>
通过扩展现有组件创建自定义组件:
tsx
import { View, Text, styled } from '@tamagui/core'

// 简单的样式化组件
export const Card = styled(View, {
  padding: '$4',
  backgroundColor: '$background',
  borderRadius: '$4',
  borderWidth: 1,
  borderColor: '$borderColor',
})

// 带变体的组件
export const Button = styled(View, {
  padding: '$3',
  borderRadius: '$2',
  backgroundColor: '$blue10',
  cursor: 'pointer',
  
  variants: {
    variant: {
      primary: {
        backgroundColor: '$blue10',
      },
      secondary: {
        backgroundColor: '$gray5',
      },
      outlined: {
        backgroundColor: 'transparent',
        borderWidth: 1,
        borderColor: '$borderColor',
      },
    },
    size: {
      small: { padding: '$2' },
      medium: { padding: '$3' },
      large: { padding: '$4' },
    },
  } as const,
  
  defaultVariants: {
    variant: 'primary',
    size: 'medium',
  },
})

// 使用示例
<Button variant="outlined" size="large">Click Me</Button>

2. Core Layout Components

2. 核心布局组件

Stacks are the foundation of Tamagui layouts:
tsx
import { XStack, YStack, ZStack, Text, Button } from 'tamagui'

export function LayoutExample() {
  return (
    <YStack gap="$4" padding="$4">
      {/* Horizontal stack */}
      <XStack gap="$2" justifyContent="space-between" alignItems="center">
        <Text>Label</Text>
        <Button>Action</Button>
      </XStack>
      
      {/* Vertical stack with responsive gap */}
      <YStack 
        gap="$4"
        $gtSm={{ gap: '$6' }}  // Larger gap on small+ screens
      >
        <Card>Item 1</Card>
        <Card>Item 2</Card>
      </YStack>
      
      {/* Overlay stack (position: relative) */}
      <ZStack width={300} height={200}>
        <View backgroundColor="$blue5" fullscreen />
        <Text position="absolute" top="$4" left="$4">
          Overlay Text
        </Text>
      </ZStack>
    </YStack>
  )
}
Stacks是Tamagui布局的基础:
tsx
import { XStack, YStack, ZStack, Text, Button } from 'tamagui'

export function LayoutExample() {
  return (
    <YStack gap="$4" padding="$4">
      {/* 水平堆叠 */}
      <XStack gap="$2" justifyContent="space-between" alignItems="center">
        <Text>Label</Text>
        <Button>Action</Button>
      </XStack>
      
      {/* 带响应式间距的垂直堆叠 */}
      <YStack 
        gap="$4"
        $gtSm={{ gap: '$6' }}  // 小屏及以上设备使用更大间距
      >
        <Card>Item 1</Card>
        <Card>Item 2</Card>
      </YStack>
      
      {/* 覆盖堆叠(position: relative) */}
      <ZStack width={300} height={200}>
        <View backgroundColor="$blue5" fullscreen />
        <Text position="absolute" top="$4" left="$4">
          Overlay Text
        </Text>
      </ZStack>
    </YStack>
  )
}

3. Using Design Tokens

3. 使用设计令牌

Design tokens provide consistent values across your app:
tsx
import { View, Text, createTamagui } from '@tamagui/core'

// Tokens are defined in createTamagui config
const config = createTamagui({
  tokens: {
    color: {
      white: '#fff',
      black: '#000',
      blue: '#0066cc',
    },
    space: {
      1: 4,
      2: 8,
      3: 12,
      4: 16,
    },
    size: {
      sm: 100,
      md: 200,
      lg: 300,
    },
    radius: {
      1: 4,
      2: 8,
      3: 12,
      4: 16,
    },
  },
})

// Use tokens with $ prefix
<View 
  padding="$4"           // Uses space.4 = 16px
  backgroundColor="$blue" // Uses color.blue
  borderRadius="$3"      // Uses radius.3 = 12px
  width="$md"            // Uses size.md = 200px
/>

// Tokens work in styled()
const StyledCard = styled(View, {
  padding: '$4',
  margin: '$2',
  backgroundColor: '$background',
  borderRadius: '$4',
})
设计令牌可在应用中提供一致的取值:
tsx
import { View, Text, createTamagui } from '@tamagui/core'

// 令牌在createTamagui配置中定义
const config = createTamagui({
  tokens: {
    color: {
      white: '#fff',
      black: '#000',
      blue: '#0066cc',
    },
    space: {
      1: 4,
      2: 8,
      3: 12,
      4: 16,
    },
    size: {
      sm: 100,
      md: 200,
      lg: 300,
    },
    radius: {
      1: 4,
      2: 8,
      3: 12,
      4: 16,
    },
  },
})

// 使用$前缀引用令牌
<View 
  padding="$4"           // 使用space.4 = 16px
  backgroundColor="$blue" // 使用color.blue
  borderRadius="$3"      // 使用radius.3 = 12px
  width="$md"            // 使用size.md = 200px
/>

// 令牌在styled()中同样适用
const StyledCard = styled(View, {
  padding: '$4',
  margin: '$2',
  backgroundColor: '$background',
  borderRadius: '$4',
})

4. Theming with 12-Step Color Scales

4. 基于12阶颜色刻度的主题设置

Tamagui uses a semantic color scale system (1-12) for consistent theming:
tsx
import { YStack, Text, Theme } from 'tamagui'

export function ThemeExample() {
  return (
    <YStack 
      backgroundColor="$color1"  // Subtle background
      borderColor="$color6"      // Regular border
      padding="$4"
    >
      <Text color="$color12">Heading (highest contrast)</Text>
      <Text color="$color11">Body text (high contrast)</Text>
      <Text color="$color10">Muted text (low contrast)</Text>
      
      {/* Apply sub-theme */}
      <Theme name="blue">
        <YStack 
          backgroundColor="$color9"  // Blue solid background
          padding="$3"
          borderRadius="$2"
        >
          <Text color="$color1">White text on blue</Text>
        </Theme>
      </YStack>
    </YStack>
  )
}

// Dynamic theme switching
import { useTheme } from '@tamagui/core'

export function ThemedComponent() {
  const theme = useTheme()
  
  // Access theme values directly
  console.log(theme.background.val) // e.g., "#ffffff"
  console.log(theme.color11.val)    // e.g., "#333333"
  
  return <Text color={theme.color11}>Themed Text</Text>
}
Tamagui使用语义化颜色刻度系统(1-12阶)实现一致的主题:
tsx
import { YStack, Text, Theme } from 'tamagui'

export function ThemeExample() {
  return (
    <YStack 
      backgroundColor="$color1"  // 浅背景色
      borderColor="$color6"      // 常规边框色
      padding="$4"
    >
      <Text color="$color12">标题(最高对比度)</Text>
      <Text color="$color11">正文文本(高对比度)</Text>
      <Text color="$color10">次要文本(低对比度)</Text>
      
      {/* 应用子主题 */}
      <Theme name="blue">
        <YStack 
          backgroundColor="$color9"  // 蓝色实色背景
          padding="$3"
          borderRadius="$2"
        >
          <Text color="$color1">蓝色背景上的白色文本</Text>
        </Theme>
      </YStack>
    </YStack>
  )
}

// 动态切换主题
import { useTheme } from '@tamagui/core'

export function ThemedComponent() {
  const theme = useTheme()
  
  // 直接访问主题值
  console.log(theme.background.val) // 例如:"#ffffff"
  console.log(theme.color11.val)    // 例如:"#333333"
  
  return <Text color={theme.color11}>主题化文本</Text>
}

5. Responsive Design with Media Queries

5. 使用媒体查询实现响应式设计

Use media query props for responsive layouts:
tsx
import { YStack, Text, useMedia } from 'tamagui'

export function ResponsiveExample() {
  const media = useMedia()
  
  return (
    <YStack
      padding="$4"
      $gtSm={{ padding: '$6' }}     // > sm breakpoint
      $gtMd={{ padding: '$8' }}     // > md breakpoint
      flexDirection="column"
      $gtLg={{ flexDirection: 'row' }}  // Row layout on large screens
    >
      <Text
        fontSize="$4"
        $gtSm={{ fontSize: '$5' }}
        $gtMd={{ fontSize: '$6' }}
      >
        Responsive Text
      </Text>
      
      {/* Conditional rendering based on media query */}
      {media.gtMd && <Text>Only on medium+ screens</Text>}
    </YStack>
  )
}

// Configure media queries in createTamagui
const config = createTamagui({
  media: {
    xs: { maxWidth: 660 },
    sm: { maxWidth: 800 },
    md: { maxWidth: 1020 },
    lg: { maxWidth: 1280 },
    xl: { maxWidth: 1420 },
    xxl: { maxWidth: 1600 },
    gtXs: { minWidth: 660 + 1 },
    gtSm: { minWidth: 800 + 1 },
    gtMd: { minWidth: 1020 + 1 },
    gtLg: { minWidth: 1280 + 1 },
  },
})
使用媒体查询属性创建响应式布局:
tsx
import { YStack, Text, useMedia } from 'tamagui'

export function ResponsiveExample() {
  const media = useMedia()
  
  return (
    <YStack
      padding="$4"
      $gtSm={{ padding: '$6' }}     // > sm断点
      $gtMd={{ padding: '$8' }}     // > md断点
      flexDirection="column"
      $gtLg={{ flexDirection: 'row' }}  // 大屏设备使用行布局
    >
      <Text
        fontSize="$4"
        $gtSm={{ fontSize: '$5' }}
        $gtMd={{ fontSize: '$6' }}
      >
        响应式文本
      </Text>
      
      {/* 根据媒体查询条件渲染 */}
      {media.gtMd && <Text>仅在中等及以上屏幕显示</Text>}
    </YStack>
  )
}

// 在createTamagui中配置媒体查询
const config = createTamagui({
  media: {
    xs: { maxWidth: 660 },
    sm: { maxWidth: 800 },
    md: { maxWidth: 1020 },
    lg: { maxWidth: 1280 },
    xl: { maxWidth: 1420 },
    xxl: { maxWidth: 1600 },
    gtXs: { minWidth: 660 + 1 },
    gtSm: { minWidth: 800 + 1 },
    gtMd: { minWidth: 1020 + 1 },
    gtLg: { minWidth: 1280 + 1 },
  },
})

6. Animations with enterStyle/exitStyle

6. 使用enterStyle/exitStyle添加动画

Add smooth animations to any component:
tsx
import { YStack, Button, AnimatePresence } from 'tamagui'
import { useState } from 'react'

export function AnimationExample() {
  const [show, setShow] = useState(false)
  
  return (
    <YStack gap="$4">
      <Button onPress={() => setShow(!show)}>
        Toggle
      </Button>
      
      <AnimatePresence>
        {show && (
          <YStack
            key="animated-box"
            animation="quick"
            enterStyle={{ 
              opacity: 0, 
              y: -20,
              scale: 0.9,
            }}
            exitStyle={{ 
              opacity: 0,
              y: 20,
              scale: 0.9,
            }}
            opacity={1}
            y={0}
            scale={1}
            backgroundColor="$blue5"
            padding="$4"
            borderRadius="$4"
          >
            <Text>Animated Content</Text>
          </YStack>
        )}
      </AnimatePresence>
    </YStack>
  )
}

// Define animations in createTamagui
import { createAnimations } from '@tamagui/animations-react-native'

const animations = createAnimations({
  quick: {
    type: 'spring',
    damping: 20,
    mass: 1.2,
    stiffness: 250,
  },
  bouncy: {
    type: 'spring',
    damping: 10,
    mass: 0.9,
    stiffness: 100,
  },
})
为任意组件添加流畅动画:
tsx
import { YStack, Button, AnimatePresence } from 'tamagui'
import { useState } from 'react'

export function AnimationExample() {
  const [show, setShow] = useState(false)
  
  return (
    <YStack gap="$4">
      <Button onPress={() => setShow(!show)}>
        切换
      </Button>
      
      <AnimatePresence>
        {show && (
          <YStack
            key="animated-box"
            animation="quick"
            enterStyle={{ 
              opacity: 0, 
              y: -20,
              scale: 0.9,
            }}
            exitStyle={{ 
              opacity: 0,
              y: 20,
              scale: 0.9,
            }}
            opacity={1}
            y={0}
            scale={1}
            backgroundColor="$blue5"
            padding="$4"
            borderRadius="$4"
          >
            <Text>动画内容</Text>
          </YStack>
        )}
      </AnimatePresence>
    </YStack>
  )
}

// 在createTamagui中定义动画
import { createAnimations } from '@tamagui/animations-react-native'

const animations = createAnimations({
  quick: {
    type: 'spring',
    damping: 20,
    mass: 1.2,
    stiffness: 250,
  },
  bouncy: {
    type: 'spring',
    damping: 10,
    mass: 0.9,
    stiffness: 100,
  },
})

7. Compound Components with createStyledContext

7. 使用createStyledContext构建复合组件

Build cohesive component families:
tsx
import { createStyledContext, styled, View, Text } from '@tamagui/core'
import { withStaticProperties } from '@tamagui/helpers'

// Define context
const CardContext = createStyledContext({
  size: '$4' as any,
})

// Create base component
const CardFrame = styled(View, {
  backgroundColor: '$background',
  borderRadius: '$4',
  borderWidth: 1,
  borderColor: '$borderColor',
  
  context: CardContext,
  
  variants: {
    size: {
      small: { padding: '$3' },
      medium: { padding: '$4' },
      large: { padding: '$6' },
    },
  } as const,
})

// Create child components that consume context
const CardTitle = styled(Text, {
  context: CardContext,
  fontWeight: 'bold',
  
  variants: {
    size: {
      small: { fontSize: '$4' },
      medium: { fontSize: '$5' },
      large: { fontSize: '$6' },
    },
  } as const,
})

const CardDescription = styled(Text, {
  context: CardContext,
  color: '$color11',
  
  variants: {
    size: {
      small: { fontSize: '$3' },
      medium: { fontSize: '$4' },
      large: { fontSize: '$5' },
    },
  } as const,
})

// Compose with static properties
export const Card = withStaticProperties(CardFrame, {
  Title: CardTitle,
  Description: CardDescription,
})

// Usage - size cascades to all children
<Card size="large">
  <Card.Title>Large Card</Card.Title>
  <Card.Description>
    All text automatically sized large
  </Card.Description>
</Card>
构建内聚的组件家族:
tsx
import { createStyledContext, styled, View, Text } from '@tamagui/core'
import { withStaticProperties } from '@tamagui/helpers'

// 定义上下文
const CardContext = createStyledContext({
  size: '$4' as any,
})

// 创建基础组件
const CardFrame = styled(View, {
  backgroundColor: '$background',
  borderRadius: '$4',
  borderWidth: 1,
  borderColor: '$borderColor',
  
  context: CardContext,
  
  variants: {
    size: {
      small: { padding: '$3' },
      medium: { padding: '$4' },
      large: { padding: '$6' },
    },
  } as const,
})

// 创建消费上下文的子组件
const CardTitle = styled(Text, {
  context: CardContext,
  fontWeight: 'bold',
  
  variants: {
    size: {
      small: { fontSize: '$4' },
      medium: { fontSize: '$5' },
      large: { fontSize: '$6' },
    },
  } as const,
})

const CardDescription = styled(Text, {
  context: CardContext,
  color: '$color11',
  
  variants: {
    size: {
      small: { fontSize: '$3' },
      medium: { fontSize: '$4' },
      large: { fontSize: '$5' },
    },
  } as const,
})

// 组合为带静态属性的组件
export const Card = withStaticProperties(CardFrame, {
  Title: CardTitle,
  Description: CardDescription,
})

// 使用示例 - 尺寸会自动传递给所有子组件
<Card size="large">
  <Card.Title>大尺寸卡片</Card.Title>
  <Card.Description>
    所有文本自动使用大尺寸
  </Card.Description>
</Card>

8. Form Component Example

8. 表单组件示例

tsx
import { Button, Dialog, Input, Label, XStack, YStack } from 'tamagui'

export function LoginForm() {
  return (
    <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>
      
      <YStack gap="$2">
        <Label htmlFor="password">Password</Label>
        <Input 
          id="password" 
          secureTextEntry
          placeholder="••••••••"
        />
      </YStack>
      
      <XStack gap="$2" justifyContent="flex-end">
        <Button variant="outlined">Cancel</Button>
        <Button theme="blue">Sign In</Button>
      </XStack>
    </YStack>
  )
}
tsx
import { Button, Dialog, Input, Label, XStack, YStack } from 'tamagui'

export function LoginForm() {
  return (
    <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>
      
      <YStack gap="$2">
        <Label htmlFor="password">密码</Label>
        <Input 
          id="password" 
          secureTextEntry
          placeholder="••••••••"
        />
      </YStack>
      
      <XStack gap="$2" justifyContent="flex-end">
        <Button variant="outlined">取消</Button>
        <Button theme="blue">登录</Button>
      </XStack>
    </YStack>
  )
}

9. Dialog Pattern

9. 对话框示例

tsx
import { Button, Dialog, XStack, YStack, H2, Paragraph } from 'tamagui'

export function DialogExample() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button>Open Dialog</Button>
      </Dialog.Trigger>

      <Dialog.Portal>
        <Dialog.Overlay
          key="overlay"
          animation="quick"
          opacity={0.5}
          enterStyle={{ opacity: 0 }}
          exitStyle={{ opacity: 0 }}
        />

        <Dialog.Content
          bordered
          elevate
          key="content"
          animation={['quick', { opacity: { overshootClamping: true } }]}
          enterStyle={{ x: 0, y: -20, opacity: 0, scale: 0.9 }}
          exitStyle={{ x: 0, y: 10, opacity: 0, scale: 0.95 }}
          gap="$4"
        >
          <Dialog.Title>Confirm Action</Dialog.Title>
          <Dialog.Description>
            Are you sure you want to proceed?
          </Dialog.Description>

          <XStack alignSelf="flex-end" gap="$2">
            <Dialog.Close displayWhenAdapted asChild>
              <Button variant="outlined">Cancel</Button>
            </Dialog.Close>
            <Dialog.Close asChild>
              <Button theme="blue">Confirm</Button>
            </Dialog.Close>
          </XStack>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog>
  )
}
tsx
import { Button, Dialog, XStack, YStack, H2, Paragraph } from 'tamagui'

export function DialogExample() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button>打开对话框</Button>
      </Dialog.Trigger>

      <Dialog.Portal>
        <Dialog.Overlay
          key="overlay"
          animation="quick"
          opacity={0.5}
          enterStyle={{ opacity: 0 }}
          exitStyle={{ opacity: 0 }}
        />

        <Dialog.Content
          bordered
          elevate
          key="content"
          animation={['quick', { opacity: { overshootClamping: true } }]}
          enterStyle={{ x: 0, y: -20, opacity: 0, scale: 0.9 }}
          exitStyle={{ x: 0, y: 10, opacity: 0, scale: 0.95 }}
          gap="$4"
        >
          <Dialog.Title>确认操作</Dialog.Title>
          <Dialog.Description>
            确定要继续吗?
          </Dialog.Description>

          <XStack alignSelf="flex-end" gap="$2">
            <Dialog.Close displayWhenAdapted asChild>
              <Button variant="outlined">取消</Button>
            </Dialog.Close>
            <Dialog.Close asChild>
              <Button theme="blue">确认</Button>
            </Dialog.Close>
          </XStack>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog>
  )
}

10. Adapt Pattern (Mobile Sheet on Small Screens)

10. 适配模式(小屏设备使用Sheet)

tsx
import { Button, Dialog, Sheet, Adapt } from 'tamagui'

export function AdaptiveDialog() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button>Open</Button>
      </Dialog.Trigger>

      <Adapt when="sm" platform="touch">
        <Sheet animation="medium" zIndex={200000} modal dismissOnSnapToBottom>
          <Sheet.Frame padding="$4" gap="$4">
            <Adapt.Contents />
          </Sheet.Frame>
          <Sheet.Overlay 
            animation="lazy"
            enterStyle={{ opacity: 0 }}
            exitStyle={{ opacity: 0 }}
          />
        </Sheet>
      </Adapt>

      <Dialog.Portal>
        <Dialog.Overlay />
        <Dialog.Content>
          {/* Content shows as Dialog on desktop, Sheet on mobile */}
          <Dialog.Title>Adaptive UI</Dialog.Title>
          <Dialog.Description>
            This is a dialog on desktop, sheet on mobile
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog>
  )
}

tsx
import { Button, Dialog, Sheet, Adapt } from 'tamagui'

export function AdaptiveDialog() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button>打开</Button>
      </Dialog.Trigger>

      <Adapt when="sm" platform="touch">
        <Sheet animation="medium" zIndex={200000} modal dismissOnSnapToBottom>
          <Sheet.Frame padding="$4" gap="$4">
            <Adapt.Contents />
          </Sheet.Frame>
          <Sheet.Overlay 
            animation="lazy"
            enterStyle={{ opacity: 0 }}
            exitStyle={{ opacity: 0 }}
          />
        </Sheet>
      </Adapt>

      <Dialog.Portal>
        <Dialog.Overlay />
        <Dialog.Content>
          {/* 在桌面端显示为Dialog,移动端显示为Sheet */}
          <Dialog.Title>自适应UI</Dialog.Title>
          <Dialog.Description>
            这在桌面端是对话框,在移动端是底部弹窗
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog>
  )
}

Bento Components Overview

Bento组件概述

Premium components from
@tamagui/bento
(requires Bento license for production use).
来自
@tamagui/bento
的高级组件(生产环境使用需要Bento授权)。

Forms System

表单系统

Composable input components with React Hook Form and Zod integration:
tsx
import { Input } from '@tamagui/bento/components/inputsParts'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
})

export function BentoForm() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
  })
  
  return (
    <YStack gap="$4">
      <Input size="$4">
        <Input.Label htmlFor="email">Email</Input.Label>
        <Input.Box>
          <Input.Section>
            <Mail size={16} />
          </Input.Section>
          <Input.Area 
            id="email" 
            placeholder="email@example.com"
            {...register('email')}
          />
        </Input.Box>
        {errors.email && (
          <Input.Info color="$red10">{errors.email.message}</Input.Info>
        )}
      </Input>
      
      <Input size="$4">
        <Input.Label htmlFor="password">Password</Input.Label>
        <Input.Box>
          <Input.Area 
            id="password" 
            secureTextEntry
            placeholder="••••••••"
            {...register('password')}
          />
          <Input.Section>
            <Input.Button>
              <Eye size={16} />
            </Input.Button>
          </Input.Section>
        </Input.Box>
        {errors.password && (
          <Input.Info color="$red10">{errors.password.message}</Input.Info>
        )}
      </Input>
    </YStack>
  )
}
Key Features:
  • Input.Label - Accessible label with
    htmlFor
    binding
  • Input.Box - Focus-aware container with
    FocusContext
  • Input.Section - Layout sections for icons/buttons
  • Input.Area - Actual text input
  • Input.Icon - Themed icon container
  • Input.Button - Action button within input
  • Input.Info - Helper/error text
与React Hook Form和Zod集成的可组合输入组件:
tsx
import { Input } from '@tamagui/bento/components/inputsParts'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const schema = z.object({
  email: z.string().email('无效邮箱'),
  password: z.string().min(8, '密码至少8个字符'),
})

export function BentoForm() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
  })
  
  return (
    <YStack gap="$4">
      <Input size="$4">
        <Input.Label htmlFor="email">邮箱</Input.Label>
        <Input.Box>
          <Input.Section>
            <Mail size={16} />
          </Input.Section>
          <Input.Area 
            id="email" 
            placeholder="email@example.com"
            {...register('email')}
          />
        </Input.Box>
        {errors.email && (
          <Input.Info color="$red10">{errors.email.message}</Input.Info>
        )}
      </Input>
      
      <Input size="$4">
        <Input.Label htmlFor="password">密码</Input.Label>
        <Input.Box>
          <Input.Area 
            id="password" 
            secureTextEntry
            placeholder="••••••••"
            {...register('password')}
          />
          <Input.Section>
            <Input.Button>
              <Eye size={16} />
            </Input.Button>
          </Input.Section>
        </Input.Box>
        {errors.password && (
          <Input.Info color="$red10">{errors.password.message}</Input.Info>
        )}
      </Input>
    </YStack>
  )
}
核心特性:
  • Input.Label - 带
    htmlFor
    绑定的可访问性标签
  • Input.Box - 支持焦点感知的容器,带有
    FocusContext
  • Input.Section - 用于图标/按钮的布局区域
  • Input.Area - 实际的文本输入区域
  • Input.Icon - 主题化图标容器
  • Input.Button - 输入框内的操作按钮
  • Input.Info - 辅助/错误文本

Tables with TanStack Table

基于TanStack Table的表格组件

Production-ready data tables with sorting, filtering, and pagination:
tsx
import { DataTable } from '@tamagui/bento/components/Table'
import { createColumnHelper } from '@tanstack/react-table'

type User = {
  id: number
  name: string
  email: string
  role: string
}

const columnHelper = createColumnHelper<User>()

const columns = [
  columnHelper.accessor('name', {
    header: 'Name',
    cell: info => info.getValue(),
  }),
  columnHelper.accessor('email', {
    header: 'Email',
    cell: info => info.getValue(),
  }),
  columnHelper.accessor('role', {
    header: 'Role',
    cell: info => <Text textTransform="capitalize">{info.getValue()}</Text>,
  }),
]

export function UsersTable({ users }: { users: User[] }) {
  return (
    <DataTable
      data={users}
      columns={columns}
      enableSorting
      enableFiltering
      enablePagination
      pageSize={10}
    />
  )
}
支持排序、筛选和分页的生产级数据表格:
tsx
import { DataTable } from '@tamagui/bento/components/Table'
import { createColumnHelper } from '@tanstack/react-table'

type User = {
  id: number
  name: string
  email: string
  role: string
}

const columnHelper = createColumnHelper<User>()

const columns = [
  columnHelper.accessor('name', {
    header: '姓名',
    cell: info => info.getValue(),
  }),
  columnHelper.accessor('email', {
    header: '邮箱',
    cell: info => info.getValue(),
  }),
  columnHelper.accessor('role', {
    header: '角色',
    cell: info => <Text textTransform="capitalize">{info.getValue()}</Text>,
  }),
]

export function UsersTable({ users }: { users: User[] }) {
  return (
    <DataTable
      data={users}
      columns={columns}
      enableSorting
      enableFiltering
      enablePagination
      pageSize={10}
    />
  )
}

Virtualized Lists

虚拟化列表

Efficient list rendering with FlatList patterns:
tsx
import { FlatList } from '@tamagui/bento/components/List'
import { YStack, Text } from 'tamagui'

export function VirtualizedList({ items }: { items: string[] }) {
  return (
    <FlatList
      data={items}
      keyExtractor={(item, index) => `${item}-${index}`}
      renderItem={({ item }) => (
        <YStack padding="$4" borderBottomWidth={1} borderColor="$borderColor">
          <Text>{item}</Text>
        </YStack>
      )}
      estimatedItemSize={60}
    />
  )
}
Additional Patterns:
  • Masonry layouts - Pinterest-style grids
  • Horizontal carousels - Swipeable lists
  • Animated lists - Enter/exit animations
  • Pull to refresh - Mobile-friendly refresh

基于FlatList模式的高效列表渲染:
tsx
import { FlatList } from '@tamagui/bento/components/List'
import { YStack, Text } from 'tamagui'

export function VirtualizedList({ items }: { items: string[] }) {
  return (
    <FlatList
      data={items}
      keyExtractor={(item, index) => `${item}-${index}`}
      renderItem={({ item }) => (
        <YStack padding="$4" borderBottomWidth={1} borderColor="$borderColor">
          <Text>{item}</Text>
        </YStack>
      )}
      estimatedItemSize={60}
    />
  )
}
其他模式:
  • 瀑布流布局 - Pinterest风格网格
  • 水平轮播 - 可滑动列表
  • 动画列表 - 进入/退出动画
  • 下拉刷新 - 移动端友好的刷新方式

Takeout Stack Overview

Takeout技术栈概述

Full-stack starter kit from Tamagui Takeout (requires license).
来自Tamagui Takeout的全栈启动套件(需要授权)。

One.js Routing

One.js路由

File-based routing with SSG/SSR/SPA modes:
app/
  _layout.tsx              # Root layout
  index.tsx                # / (home page)
  blog/
    [slug].tsx             # /blog/my-post (dynamic route)
  (auth)/                  # Route group (no URL nesting)
    login.tsx              # /login
    signup.tsx             # /signup
  +not-found.tsx           # Custom 404 page
Type-safe navigation:
tsx
import { Link, useRouter, useParams } from 'one'

export function Navigation() {
  const router = useRouter()
  
  return (
    <YStack gap="$2">
      {/* Declarative navigation */}
      <Link href="/">Home</Link>
      <Link href="/blog/hello-world">Blog Post</Link>
      
      {/* Programmatic navigation */}
      <Button onPress={() => router.push('/about')}>
        Go to About
      </Button>
    </YStack>
  )
}

// Dynamic routes
export function BlogPost() {
  const { slug } = useParams<{ slug: string }>()
  return <Text>Post: {slug}</Text>
}
Server-side data loading:
tsx
import { createLoader } from 'one'

export const loader = createLoader(async (context) => {
  const post = await db.query.posts.findFirst({
    where: eq(posts.slug, context.params.slug),
  })
  
  return { post }
})

export default function BlogPost() {
  const { post } = useLoader(loader)
  return <Text>{post.title}</Text>
}
支持SSG/SSR/SPA模式的文件系统路由:
app/
  _layout.tsx              # 根布局
  index.tsx                # /(首页)
  blog/
    [slug].tsx             # /blog/my-post(动态路由)
  (auth)/                  # 路由组(无URL嵌套)
    login.tsx              # /login
    signup.tsx             # /signup
  +not-found.tsx           # 自定义404页面
类型安全的导航:
tsx
import { Link, useRouter, useParams } from 'one'

export function Navigation() {
  const router = useRouter()
  
  return (
    <YStack gap="$2">
      {/* 声明式导航 */}
      <Link href="/">首页</Link>
      <Link href="/blog/hello-world">博客文章</Link>
      
      {/* 编程式导航 */}
      <Button onPress={() => router.push('/about')}>
        前往关于页
      </Button>
    </YStack>
  )
}

// 动态路由
export function BlogPost() {
  const { slug } = useParams<{ slug: string }>()
  return <Text>文章:{slug}</Text>
}
服务端数据加载:
tsx
import { createLoader } from 'one'

export const loader = createLoader(async (context) => {
  const post = await db.query.posts.findFirst({
    where: eq(posts.slug, context.params.slug),
  })
  
  return { post }
})

export default function BlogPost() {
  const { post } = useLoader(loader)
  return <Text>{post.title}</Text>
}

Better Auth Integration

Better Auth集成

Complete authentication system with multiple strategies:
tsx
import { signIn, signOut, useSession } from '~/lib/auth'

export function AuthExample() {
  const session = useSession()
  
  if (!session.user) {
    return (
      <YStack gap="$4">
        {/* Email/password */}
        <Button onPress={() => signIn.email({ email, password })}>
          Sign In
        </Button>
        
        {/* OAuth */}
        <Button onPress={() => signIn.social({ provider: 'github' })}>
          Sign in with GitHub
        </Button>
        
        {/* Magic link */}
        <Button onPress={() => signIn.magicLink({ email })}>
          Send Magic Link
        </Button>
      </YStack>
    )
  }
  
  return (
    <YStack gap="$2">
      <Text>Welcome, {session.user.name}</Text>
      <Button onPress={() => signOut()}>Sign Out</Button>
    </YStack>
  )
}
Features:
  • Email/password authentication
  • OAuth (GitHub, Google, etc.)
  • Magic links (email)
  • OTP (email/phone)
  • Session management
  • JWT tokens for native apps
支持多种策略的完整认证系统:
tsx
import { signIn, signOut, useSession } from '~/lib/auth'

export function AuthExample() {
  const session = useSession()
  
  if (!session.user) {
    return (
      <YStack gap="$4">
        {/* 邮箱/密码登录 */}
        <Button onPress={() => signIn.email({ email, password })}>
          登录
        </Button>
        
        {/* OAuth登录 */}
        <Button onPress={() => signIn.social({ provider: 'github' })}>
          使用GitHub登录
        </Button>
        
        {/* 魔法链接登录 */}
        <Button onPress={() => signIn.magicLink({ email })}>
          发送魔法链接
        </Button>
      </YStack>
    )
  }
  
  return (
    <YStack gap="$2">
      <Text>欢迎,{session.user.name}</Text>
      <Button onPress={() => signOut()}>退出登录</Button>
    </YStack>
  )
}
特性:
  • 邮箱/密码认证
  • OAuth(GitHub、Google等)
  • 魔法链接(邮箱)
  • OTP(邮箱/手机)
  • 会话管理
  • 面向原生应用的JWT令牌

Drizzle ORM (Database)

Drizzle ORM(数据库)

Type-safe database queries with PostgreSQL:
tsx
import { db } from '~/db'
import { posts, users } from '~/db/schema'
import { eq, desc } from 'drizzle-orm'

// Define schema
export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  slug: text('slug').notNull().unique(),
  content: text('content'),
  authorId: integer('author_id').references(() => users.id),
  createdAt: timestamp('created_at').defaultNow(),
})

// Query
const allPosts = await db.query.posts.findMany({
  orderBy: [desc(posts.createdAt)],
  with: {
    author: true,  // Join with users
  },
})

// Insert
await db.insert(posts).values({
  title: 'My Post',
  slug: 'my-post',
  content: 'Post content...',
  authorId: 1,
})

// Update
await db.update(posts)
  .set({ title: 'Updated Title' })
  .where(eq(posts.id, 1))

// Delete
await db.delete(posts).where(eq(posts.id, 1))
基于PostgreSQL的类型安全数据库查询:
tsx
import { db } from '~/db'
import { posts, users } from '~/db/schema'
import { eq, desc } from 'drizzle-orm'

// 定义表结构
export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  slug: text('slug').notNull().unique(),
  content: text('content'),
  authorId: integer('author_id').references(() => users.id),
  createdAt: timestamp('created_at').defaultNow(),
})

// 查询所有文章
const allPosts = await db.query.posts.findMany({
  orderBy: [desc(posts.createdAt)],
  with: {
    author: true,  // 关联用户表
  },
})

// 插入文章
await db.insert(posts).values({
  title: '我的文章',
  slug: 'my-post',
  content: '文章内容...',
  authorId: 1,
})

// 更新文章
await db.update(posts)
  .set({ title: '更新后的标题' })
  .where(eq(posts.id, 1))

// 删除文章
await db.delete(posts).where(eq(posts.id, 1))

Zero Sync (Real-time Data)

Zero Sync(实时数据)

Offline-first real-time synchronization:
tsx
import { useQuery, useMutation } from '@rocicorp/zero/react'
import { z } from '@rocicorp/zero'

// Define schema
const todoSchema = z.object({
  id: z.string(),
  text: z.string(),
  completed: z.boolean(),
  createdAt: z.number(),
})

export function TodoList() {
  // Real-time query (updates automatically)
  const [todos] = useQuery(q => q.todos.orderBy('createdAt', 'desc'))
  
  // Optimistic mutation
  const [addTodo] = useMutation(async (tx, text: string) => {
    await tx.todos.insert({
      id: crypto.randomUUID(),
      text,
      completed: false,
      createdAt: Date.now(),
    })
  })
  
  const [toggleTodo] = useMutation(async (tx, id: string) => {
    const todo = await tx.todos.get(id)
    if (todo) {
      await tx.todos.update({
        id,
        completed: !todo.completed,
      })
    }
  })
  
  return (
    <YStack gap="$2">
      {todos.map(todo => (
        <XStack key={todo.id} gap="$2">
          <Checkbox 
            checked={todo.completed}
            onCheckedChange={() => toggleTodo(todo.id)}
          />
          <Text>{todo.text}</Text>
        </XStack>
      ))}
    </YStack>
  )
}
Features:
  • Real-time synchronization across devices
  • Offline-first architecture
  • Optimistic mutations (instant UI updates)
  • Automatic conflict resolution
  • Type-safe queries with Zod
  • Client-side replica for 0-latency queries

支持离线优先的实时数据同步:
tsx
import { useQuery, useMutation } from '@rocicorp/zero/react'
import { z } from '@rocicorp/zero'

// 定义数据结构
const todoSchema = z.object({
  id: z.string(),
  text: z.string(),
  completed: z.boolean(),
  createdAt: z.number(),
})

export function TodoList() {
  // 实时查询(自动更新)
  const [todos] = useQuery(q => q.todos.orderBy('createdAt', 'desc'))
  
  // 乐观更新
  const [addTodo] = useMutation(async (tx, text: string) => {
    await tx.todos.insert({
      id: crypto.randomUUID(),
      text,
      completed: false,
      createdAt: Date.now(),
    })
  })
  
  const [toggleTodo] = useMutation(async (tx, id: string) => {
    const todo = await tx.todos.get(id)
    if (todo) {
      await tx.todos.update({
        id,
        completed: !todo.completed,
      })
    }
  })
  
  return (
    <YStack gap="$2">
      {todos.map(todo => (
        <XStack key={todo.id} gap="$2">
          <Checkbox 
            checked={todo.completed}
            onCheckedChange={() => toggleTodo(todo.id)}
          />
          <Text>{todo.text}</Text>
        </XStack>
      ))}
    </YStack>
  )
}
特性:
  • 跨设备实时数据同步
  • 离线优先架构
  • 乐观更新(即时UI反馈)
  • 自动冲突解决
  • 基于Zod的类型安全查询
  • 客户端副本实现0延迟查询

Reference Guide

参考指南

For detailed documentation on specific topics, refer to these reference files:
TopicReference FileDescription
Core Styling
core-styling.md
styled()
API, variant systems,
createStyledContext
, composition patterns, TypeScript integration
Components
components.md
Button, Dialog, Sheet, Input, Select, Tabs, Switch, Popover, Stacks, Adapt pattern
Theming
theming.md
12-step color scale system, theme creation, dynamic theme switching,
useTheme
hook
Animations
animations.md
Animation drivers (CSS, React Native, Reanimated, Motion),
enterStyle
/
exitStyle
,
AnimatePresence
Configuration
configuration.md
createTamagui
function, tokens, themes, fonts, media queries, shorthands
Compiler
compiler.md
Static extraction, Babel optimization, atomic CSS generation, platform-specific setup
Bento Forms
bento-forms.md
React Hook Form integration, Zod validation, composable input system, accessibility
Bento Tables
bento-tables.md
TanStack Table v8 integration, sorting, pagination, filtering, responsive layouts
Bento Lists
bento-lists.md
FlatList patterns, virtualized lists, masonry layouts, performance optimization
Takeout Routing
takeout-routing.md
One.js file-based routing, SSG/SSR/SPA modes, dynamic routes, server-side data loading
Takeout Auth
takeout-auth.md
Better Auth integration, session management, OAuth, magic links, OTP
Takeout Database
takeout-database.md
Drizzle ORM setup, schema definitions, type-safe queries, migrations
Takeout Zero
takeout-zero.md
Zero Sync for real-time data, offline-first architecture, optimistic mutations
Breaking Changes
breaking-changes-and-new-features.md
Migration guide from older versions, config v4/v5 differences

如需特定主题的详细文档,请参考以下参考文件:
主题参考文件描述
核心样式
core-styling.md
styled()
API、变体系统、
createStyledContext
、组合模式、TypeScript集成
组件
components.md
Button、Dialog、Sheet、Input、Select、Tabs、Switch、Popover、Stacks、Adapt模式
主题设置
theming.md
12阶颜色刻度系统、主题创建、动态主题切换、
useTheme
钩子
动画
animations.md
动画驱动(CSS、React Native、Reanimated、Motion)、
enterStyle
/
exitStyle
AnimatePresence
配置
configuration.md
createTamagui
函数、令牌、主题、字体、媒体查询、简写方式
编译器
compiler.md
静态提取、Babel优化、原子化CSS生成、平台特定设置
Bento表单
bento-forms.md
React Hook Form集成、Zod验证、可组合输入系统、可访问性
Bento表格
bento-tables.md
TanStack Table v8集成、排序、分页、筛选、响应式布局
Bento列表
bento-lists.md
FlatList模式、虚拟化列表、瀑布流布局、性能优化
Takeout路由
takeout-routing.md
One.js文件系统路由、SSG/SSR/SPA模式、动态路由、服务端数据加载
Takeout认证
takeout-auth.md
Better Auth集成、会话管理、OAuth、魔法链接、OTP
Takeout数据库
takeout-database.md
Drizzle ORM设置、表结构定义、类型安全查询、迁移
Takeout Zero
takeout-zero.md
用于实时数据的Zero Sync、离线优先架构、乐观更新
重大变更
breaking-changes-and-new-features.md
从旧版本迁移指南、配置v4/v5差异

Common Pitfalls

常见陷阱

1. Token Usage

1. 令牌使用

❌ Wrong:
tsx
<View padding={16} />  // Hard-coded value
<View padding="16px" /> // String with unit
✅ Correct:
tsx
<View padding="$4" />  // Token reference
<View padding={16} />  // Number is OK if intentional (not extracted by compiler)
❌ 错误用法:
tsx
<View padding={16} />  // 硬编码值
<View padding="16px" /> // 带单位的字符串
✅ 正确用法:
tsx
<View padding="$4" />  // 引用令牌
<View padding={16} />  // 如果是故意使用,数字是允许的(不会被编译器提取)

2. Variant Definitions

2. 变体定义

❌ Wrong:
tsx
// Missing `as const`
variants: {
  size: {
    small: { padding: '$2' },
    large: { padding: '$4' },
  },
}
✅ Correct:
tsx
// Must use `as const` for proper TypeScript inference
variants: {
  size: {
    small: { padding: '$2' },
    large: { padding: '$4' },
  },
} as const
❌ 错误用法:
tsx
// 缺少`as const`
variants: {
  size: {
    small: { padding: '$2' },
    large: { padding: '$4' },
  },
}
✅ 正确用法:
tsx
// 必须使用`as const`以获得正确的TypeScript类型推断
variants: {
  size: {
    small: { padding: '$2' },
    large: { padding: '$4' },
  },
} as const

3. Platform-Specific Styles

3. 平台特定样式

❌ Wrong:
tsx
// Using platform detection in styled()
const Component = styled(View, {
  padding: Platform.OS === 'web' ? 10 : 20,  // Won't extract
})
✅ Correct:
tsx
// Use platform prop modifiers
const Component = styled(View, {
  padding: 20,
  $platform-web: {
    padding: 10,
  },
})
❌ 错误用法:
tsx
// 在styled()中使用平台检测
const Component = styled(View, {
  padding: Platform.OS === 'web' ? 10 : 20,  // 不会被提取
})
✅ 正确用法:
tsx
// 使用平台属性修饰符
const Component = styled(View, {
  padding: 20,
  $platform-web: {
    padding: 10,
  },
})

4. Media Query Prop Order

4. 媒体查询属性顺序

❌ Wrong:
tsx
<View 
  $gtMd={{ padding: '$8' }}  // Applied first
  padding="$4"               // Overrides media query
/>
✅ Correct:
tsx
<View 
  padding="$4"               // Base value
  $gtMd={{ padding: '$8' }}  // Overrides on medium+
/>
❌ 错误用法:
tsx
<View 
  $gtMd={{ padding: '$8' }}  // 先应用
  padding="$4"               // 覆盖媒体查询
/>
✅ 正确用法:
tsx
<View 
  padding="$4"               // 基础值
  $gtMd={{ padding: '$8' }}  // 在中等及以上屏幕覆盖基础值
/>

5. Animation Driver Mismatch

5. 动画驱动不匹配

❌ Wrong:
tsx
// Using spring physics with CSS driver
import { createAnimations } from '@tamagui/animations-css'

const animations = createAnimations({
  bouncy: {
    type: 'spring',  // CSS doesn't support springs!
    damping: 10,
  },
})
✅ Correct:
tsx
// CSS driver uses easing strings
import { createAnimations } from '@tamagui/animations-css'

const animations = createAnimations({
  bouncy: 'cubic-bezier(0.68, -0.55, 0.265, 1.55) 500ms',
})

// Or use React Native driver for springs
import { createAnimations } from '@tamagui/animations-react-native'

const animations = createAnimations({
  bouncy: {
    type: 'spring',
    damping: 10,
    stiffness: 100,
  },
})
❌ 错误用法:
tsx
// 使用CSS驱动实现弹簧物理效果
import { createAnimations } from '@tamagui/animations-css'

const animations = createAnimations({
  bouncy: {
    type: 'spring',  // CSS不支持弹簧效果!
    damping: 10,
  },
})
✅ 正确用法:
tsx
// CSS驱动使用缓动字符串
import { createAnimations } from '@tamagui/animations-css'

const animations = createAnimations({
  bouncy: 'cubic-bezier(0.68, -0.55, 0.265, 1.55) 500ms',
})

// 或者使用React Native驱动实现弹簧效果
import { createAnimations } from '@tamagui/animations-react-native'

const animations = createAnimations({
  bouncy: {
    type: 'spring',
    damping: 10,
    stiffness: 100,
  },
})

6. Context Without Static Properties

6. 上下文未结合静态属性

❌ Wrong:
tsx
// Creating compound components without withStaticProperties
const Card = styled(View, { context: CardContext })
const CardTitle = styled(Text, { context: CardContext })

// Usage requires separate imports
import { Card } from './Card'
import { CardTitle } from './CardTitle'

<Card>
  <CardTitle>Title</CardTitle>
</Card>
✅ Correct:
tsx
import { withStaticProperties } from '@tamagui/helpers'

const CardFrame = styled(View, { context: CardContext })
const CardTitle = styled(Text, { context: CardContext })

export const Card = withStaticProperties(CardFrame, {
  Title: CardTitle,
})

// Single import
<Card>
  <Card.Title>Title</Card.Title>
</Card>
❌ 错误用法:
tsx
// 创建复合组件但未使用withStaticProperties
const Card = styled(View, { context: CardContext })
const CardTitle = styled(Text, { context: CardContext })

// 使用时需要单独导入
import { Card } from './Card'
import { CardTitle } from './CardTitle'

<Card>
  <CardTitle>标题</CardTitle>
</Card>
✅ 正确用法:
tsx
import { withStaticProperties } from '@tamagui/helpers'

const CardFrame = styled(View, { context: CardContext })
const CardTitle = styled(Text, { context: CardContext })

export const Card = withStaticProperties(CardFrame, {
  Title: CardTitle,
})

// 单次导入
<Card>
  <Card.Title>标题</Card.Title>
</Card>

7. Missing AnimatePresence

7. 缺少AnimatePresence

❌ Wrong:
tsx
// Using exitStyle without AnimatePresence
{show && (
  <View 
    animation="quick"
    enterStyle={{ opacity: 0 }}
    exitStyle={{ opacity: 0 }}  // Won't animate on exit!
  />
)}
✅ Correct:
tsx
import { AnimatePresence } from 'tamagui'

<AnimatePresence>
  {show && (
    <View 
      key="animated-view"  // Key is required!
      animation="quick"
      enterStyle={{ opacity: 0 }}
      exitStyle={{ opacity: 0 }}
    />
  )}
</AnimatePresence>
❌ 错误用法:
tsx
// 使用exitStyle但未添加AnimatePresence
{show && (
  <View 
    animation="quick"
    enterStyle={{ opacity: 0 }}
    exitStyle={{ opacity: 0 }}  // 退出时不会有动画!
  />
)}
✅ 正确用法:
tsx
import { AnimatePresence } from 'tamagui'

<AnimatePresence>
  {show && (
    <View 
      key="animated-view"  // 必须添加key!
      animation="quick"
      enterStyle={{ opacity: 0 }}
      exitStyle={{ opacity: 0 }}
    />
  )}
</AnimatePresence>

8. Inline Styles vs. styled()

8. 内联样式vs styled()

❌ Wrong (Performance):
tsx
// Inline styles aren't extracted by compiler
{items.map(item => (
  <View 
    key={item.id}
    style={{ 
      padding: 16, 
      backgroundColor: '#fff',
      borderRadius: 8,
    }}
  />
))}
✅ Correct:
tsx
// Use styled() for extractable styles
const ItemView = styled(View, {
  padding: '$4',
  backgroundColor: '$background',
  borderRadius: '$2',
})

{items.map(item => (
  <ItemView key={item.id} />
))}
❌ 错误用法(性能问题):
tsx
// 内联样式不会被编译器提取
{items.map(item => (
  <View 
    key={item.id}
    style={{ 
      padding: 16, 
      backgroundColor: '#fff',
      borderRadius: 8,
    }}
  />
))}
✅ 正确用法:
tsx
// 使用styled()创建可被提取的样式
const ItemView = styled(View, {
  padding: '$4',
  backgroundColor: '$background',
  borderRadius: '$2',
})

{items.map(item => (
  <ItemView key={item.id} />
))}

9. Web vs. Native Differences

9. Web与原生平台差异

Be aware of platform-specific behaviors:
  • CSS driver - Web only, no spring physics
  • Sheet component - Better UX on mobile with touch gestures
  • Adapt pattern - Automatically switches components based on platform/screen size
  • Form attributes (type, action, method) - Web only
  • Animation drivers - Reanimated performs poorly on web, great on native
注意平台特定行为:
  • CSS驱动 - 仅支持Web,无弹簧物理效果
  • Sheet组件 - 在移动端配合触摸手势体验更好
  • Adapt模式 - 根据平台/屏幕尺寸自动切换组件
  • 表单属性(type、action、method)- 仅支持Web
  • 动画驱动 - Reanimated在Web上性能较差,在原生平台表现出色

10. Compiler Optimization

10. 编译器优化

Styles must be statically analyzable for the compiler to extract them:
❌ Won't Extract:
tsx
const dynamicPadding = isPremium ? '$6' : '$4'
<View padding={dynamicPadding} />  // Runtime dynamic value
✅ Will Extract:
tsx
<View padding={isPremium ? '$6' : '$4'} />  // Inline ternary is OK
// Or use variants
<View size={isPremium ? 'large' : 'small'} />

样式必须是可静态分析的,才能被编译器提取:
❌ 无法被提取:
tsx
const dynamicPadding = isPremium ? '$6' : '$4'
<View padding={dynamicPadding} />  // 运行时动态值
✅ 可以被提取:
tsx
<View padding={isPremium ? '$6' : '$4'} />  // 内联三元表达式是允许的
// 或者使用变体
<View size={isPremium ? 'large' : 'small'} />

Code Generation Tips

代码生成技巧

When generating Tamagui code, follow these best practices:
生成Tamagui代码时,请遵循以下最佳实践:

1. Start with Configuration

1. 从配置开始

Always ensure
createTamagui
config exists with tokens, themes, and media queries before generating components.
在生成组件之前,确保
createTamagui
配置已包含令牌、主题和媒体查询。

2. Use Tokens Consistently

2. 一致使用令牌

Prefer design tokens (
$4
,
$background
,
$color11
) over hard-coded values for extractable, themeable styles.
优先使用设计令牌(
$4
$background
$color11
)而非硬编码值,以便样式可被提取和主题化。

3. Leverage Variants for Flexibility

3. 利用变体实现灵活性

Create components with size, variant, and state variants rather than separate component definitions:
tsx
// ✅ Good - One component with variants
const Button = styled(View, {
  variants: {
    variant: { primary: {...}, secondary: {...} },
    size: { small: {...}, large: {...} },
  } as const,
})

// ❌ Avoid - Multiple separate components
const PrimaryButton = styled(View, { ... })
const SecondaryButton = styled(View, { ... })
为组件创建尺寸、变体和状态变体,而非定义多个独立组件:
tsx
// ✅ 推荐 - 单个组件带多种变体
const Button = styled(View, {
  variants: {
    variant: { primary: {...}, secondary: {...} },
    size: { small: {...}, large: {...} },
  } as const,
})

// ❌ 避免 - 多个独立组件
const PrimaryButton = styled(View, { ... })
const SecondaryButton = styled(View, { ... })

4. Compose with createStyledContext

4. 使用createStyledContext进行组合

For compound components (Card, Input, Select), use
createStyledContext
to share size and styling:
tsx
const Context = createStyledContext({ size: '$4' })
const Parent = styled(View, { context: Context, variants: {...} })
const Child = styled(Text, { context: Context, variants: {...} })
export const Component = withStaticProperties(Parent, { Child })
对于复合组件(Card、Input、Select),使用
createStyledContext
共享尺寸和样式:
tsx
const Context = createStyledContext({ size: '$4' })
const Parent = styled(View, { context: Context, variants: {...} })
const Child = styled(Text, { context: Context, variants: {...} })
export const Component = withStaticProperties(Parent, { Child })

5. Include Accessibility

5. 包含可访问性支持

Always add accessibility props:
tsx
<Input 
  id="email"
  aria-label="Email address"
  aria-describedby="email-error"
  aria-invalid={!!error}
/>
<Label htmlFor="email">Email</Label>
{error && <Text id="email-error">{error}</Text>}
始终添加可访问性属性:
tsx
<Input 
  id="email"
  aria-label="邮箱地址"
  aria-describedby="email-error"
  aria-invalid={!!error}
/>
<Label htmlFor="email">邮箱</Label>
{error && <Text id="email-error">{error}</Text>}

6. Responsive by Default

6. 默认支持响应式

Include responsive props for better UX:
tsx
<YStack
  padding="$4"
  $gtSm={{ padding: '$6' }}
  $gtMd={{ padding: '$8' }}
  flexDirection="column"
  $gtLg={{ flexDirection: 'row' }}
/>
添加响应式属性以提升用户体验:
tsx
<YStack
  padding="$4"
  $gtSm={{ padding: '$6' }}
  $gtMd={{ padding: '$8' }}
  flexDirection="column"
  $gtLg={{ flexDirection: 'row' }}
/>

7. Animation Patterns

7. 动画模式

Use
enterStyle
/
exitStyle
for enter/exit animations and wrap with
AnimatePresence
:
tsx
<AnimatePresence>
  {show && (
    <View
      key="unique-key"
      animation="quick"
      enterStyle={{ opacity: 0, y: -10 }}
      exitStyle={{ opacity: 0, y: 10 }}
      opacity={1}
      y={0}
    />
  )}
</AnimatePresence>
使用
enterStyle
/
exitStyle
实现进入/退出动画,并使用
AnimatePresence
包裹:
tsx
<AnimatePresence>
  {show && (
    <View
      key="unique-key"
      animation="quick"
      enterStyle={{ opacity: 0, y: -10 }}
      exitStyle={{ opacity: 0, y: 10 }}
      opacity={1}
      y={0}
    />
  )}
</AnimatePresence>

8. Platform Adaptation

8. 平台适配

Use the Adapt pattern for mobile-friendly UIs:
tsx
<Dialog>
  <Adapt when="sm" platform="touch">
    <Sheet>
      <Adapt.Contents />
    </Sheet>
  </Adapt>
  <Dialog.Portal>
    <Dialog.Content>...</Dialog.Content>
  </Dialog.Portal>
</Dialog>
使用Adapt模式创建移动端友好的UI:
tsx
<Dialog>
  <Adapt when="sm" platform="touch">
    <Sheet>
      <Adapt.Contents />
    </Sheet>
  </Adapt>
  <Dialog.Portal>
    <Dialog.Content>...</Dialog.Content>
  </Dialog.Portal>
</Dialog>

9. TypeScript Types

9. TypeScript类型

Always export prop types for reusable components:
tsx
import { GetProps } from '@tamagui/core'

export const MyComponent = styled(View, { ... })
export type MyComponentProps = GetProps<typeof MyComponent>
始终为可复用组件导出属性类型:
tsx
import { GetProps } from '@tamagui/core'

export const MyComponent = styled(View, { ... })
export type MyComponentProps = GetProps<typeof MyComponent>

10. Testing Considerations

10. 测试注意事项

When generating test code:
  • Use
    testID
    prop for component identification (cross-platform)
  • Mock theme context with
    TamaguiProvider
  • Test responsive behavior with media query mocks
  • Verify animations with
    AnimatePresence
    exit callbacks

生成测试代码时:
  • 使用
    testID
    属性标识组件(跨平台)
  • 使用
    TamaguiProvider
    模拟主题上下文
  • 使用媒体查询模拟测试响应式行为
  • 使用
    AnimatePresence
    退出回调验证动画

Version and Ecosystem Notes

版本与生态系统说明

Current Version: 1.144.0+
Dependencies:
  • React 19+ (or React 18.2+)
  • React Native (for native targets)
  • Expo SDK (optional, for managed workflow)
Ecosystem Packages:
PackageDescription
tamagui
Meta-package with core + components
@tamagui/core
Core styling engine
@tamagui/animations-css
CSS animation driver
@tamagui/animations-react-native
React Native animation driver
@tamagui/animations-reanimated
Reanimated v3 driver
@tamagui/animations-moti
Moti/Motion driver
@tamagui/themes
Theme creation helpers
@tamagui/colors
Radix UI color scales
@tamagui/bento
Premium components (license required)
Tamagui TakeoutFull-stack starter (license required)
Migration from v1.x:
  • Read
    breaking-changes-and-new-features.md
  • Update to v4 or v5 config format
  • Realign media queries to v4 naming
  • Replace deprecated patterns

当前版本: 1.144.0+
依赖:
  • React 19+(或React 18.2+)
  • React Native(针对原生目标平台)
  • Expo SDK(可选,用于托管工作流)
生态系统包:
描述
tamagui
包含核心库+组件的元包
@tamagui/core
核心样式引擎
@tamagui/animations-css
CSS动画驱动
@tamagui/animations-react-native
React Native动画驱动
@tamagui/animations-reanimated
Reanimated v3驱动
@tamagui/animations-moti
Moti/Motion驱动
@tamagui/themes
主题创建工具
@tamagui/colors
Radix UI颜色刻度
@tamagui/bento
高级组件(需要授权)
Tamagui Takeout全栈启动套件(需要授权)
从v1.x迁移:
  • 阅读
    breaking-changes-and-new-features.md
  • 更新到v4或v5配置格式
  • 调整媒体查询以匹配v4命名
  • 替换已废弃的模式

Quick Links

快速链接

Additional Resources

额外资源

For comprehensive examples and patterns, explore:

Last Updated: January 2026
Skill Version: 2.0
Generated From: Official Tamagui documentation (v1.144.0+)
如需完整示例和模式,请查看:

最后更新: 2026年1月
技能版本: 2.0
生成来源: Tamagui官方文档(v1.144.0+)