skills/0xbigboss/tamagui-best-practices/SKILL.md
Provides Tamagui patterns for config v4, compiler optimization, styled context, and cross-platform styling. Must use when working with Tamagui projects (tamagui.config.ts, @tamagui imports).
npx skillsauth add aiskillstore/marketplace tamagui-best-practicesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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 |
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.