AgentSkillsCN

gluestack-ui-v4:validation

GlueStack UI v4的验证清单与反模式——可用于代码审查、检验实现质量,并识别常见错误。

SKILL.md
--- frontmatter
name: gluestack-ui-v4:validation
description: Validation checklist and anti-patterns for gluestack-ui v4 - use for code review, checking implementation quality, and identifying common mistakes.

Gluestack UI v4 - Validation & Anti-Patterns

This sub-skill focuses on validating implementations, identifying anti-patterns, and ensuring code quality for gluestack-ui v4.

Validation Checklist

When reviewing code, check for:

Component Usage

  • Component usage verified against official v4 docs at https://v4.gluestack.io/ui/docs/components/${componentName}/
  • All React Native primitives replaced with Gluestack components
  • Components imported from local @/components/ui/ directory
  • GluestackUIProvider wraps the app

Component Props vs className

  • Component props used instead of className when available:
    • VStack/HStack use space prop instead of gap-* className
    • Button uses variant and size props instead of className
    • Heading/Text use size prop instead of text-* className
    • Heading/Text use bold prop instead of font-bold className
    • Other component props used where applicable

Styling

  • All color values use semantic tokens (no raw colors)
  • No inline styles where className can be used
  • All spacing values use the standard scale
  • Dark mode support using dark: prefix
  • Variants defined using tva when needed
  • className properly merged in custom components

Compound Components

  • Compound components used correctly:
    • InputIcon always wrapped in InputSlot (CRITICAL)
    • ButtonText used for all button text content
    • FormControl sub-components used (FormControlLabel, FormControlError, etc.)
    • Card sub-components used (CardHeader, CardBody, CardFooter)
    • Checkbox sub-components used (CheckboxIndicator, CheckboxIcon, CheckboxLabel)
    • All other component sub-components as per official docs

Icons

  • Icons follow priority: pre-built icons → Lucide Icons → createIcon for custom icons
  • Icons imported from @/components/ui/icon

Cross-Platform Compatibility

  • Cross-platform compatibility verified:
    • All components use Gluestack wrappers (no direct react-native imports)
    • KeyboardAvoidingView uses Gluestack wrapper
    • Tested on both native and web platforms
    • All interactions work on both platforms

Performance & Best Practices

  • Performance & best practices:
    • TypeScript types defined for components and props
    • Expensive components memoized with React.memo
    • Callbacks memoized with useCallback when needed
    • Animations use Reanimated worklets (not Animated API)
    • Safe areas handled with SafeAreaView or useSafeAreaInsets
    • Long lists use FlatList (not ScrollView + map)
    • Platform-specific code uses Platform.select
    • Tested on real devices (not just simulators)

Anti-Patterns to Avoid

❌ Don't: Mix React Native and Gluestack Components

tsx
// ❌ INCORRECT: Mixing React Native and Gluestack components
import { View, Text } from "react-native";
import { Button } from "@/components/ui/button";

<View>
  <Text>Mixed usage</Text>
  <Button>Click</Button>
</View>

Why it's bad: Loses theming, accessibility, and cross-platform consistency.

Correct approach:

tsx
// ✅ CORRECT: Use Gluestack components consistently
import { Box } from "@/components/ui/box";
import { Text } from "@/components/ui/text";
import { Button, ButtonText } from "@/components/ui/button";

<Box>
  <Text>Consistent usage</Text>
  <Button>
    <ButtonText>Click</ButtonText>
  </Button>
</Box>

❌ Don't: Use Raw Color Values

tsx
// ❌ INCORRECT: Raw color values
<Box className="bg-[#3b82f6]" />
<Text className="text-red-500" />
<Box style={{ backgroundColor: '#fff' }} />

Why it's bad: Breaks theming, dark mode won't work, inconsistent colors.

Correct approach:

tsx
// ✅ CORRECT: Use semantic tokens
<Box className="bg-primary" />
<Text className="text-destructive" />
<Box className="bg-background" />

❌ Don't: Skip Sub-Components

tsx
// ❌ INCORRECT: ButtonText is required
<Button>Click Me</Button>

// ❌ INCORRECT: InputIcon not wrapped in InputSlot
<Input>
  <InputIcon as={MailIcon} />
  <InputField />
</Input>

// ❌ INCORRECT: Missing FormControl sub-components
<FormControl>
  <Text>Email</Text>
  <InputField />
</FormControl>

Why it's bad: Components won't render correctly, breaks styling and accessibility.

Correct approach:

tsx
// ✅ CORRECT: Proper sub-component usage
<Button>
  <ButtonText>Click Me</ButtonText>
</Button>

// ✅ CORRECT: InputIcon wrapped in InputSlot
<Input>
  <InputSlot>
    <InputIcon as={MailIcon} />
  </InputSlot>
  <InputField />
</Input>

// ✅ CORRECT: FormControl with proper sub-components
<FormControl>
  <FormControlLabel>
    <FormControlLabelText>Email</FormControlLabelText>
  </FormControlLabel>
  <Input>
    <InputField />
  </Input>
</FormControl>

❌ Don't: Use Inline Styles When className Works

tsx
// ❌ INCORRECT: Inline styles for static values
<Box style={{ padding: 16, backgroundColor: '#fff' }} />
<Text style={{ fontSize: 18, fontWeight: 'bold' }} />

Why it's bad: Bypasses optimization, breaks theming, harder to maintain.

Correct approach:

tsx
// ✅ CORRECT: Use className
<Box className="p-4 bg-background" />
<Text size="lg" bold className="text-foreground" />

❌ Don't: Use Arbitrary Spacing Values

tsx
// ❌ INCORRECT: Arbitrary spacing values
<Box className="p-[13px] m-[27px]" />
<Box style={{ padding: 13, margin: 27 }} />
<VStack className="gap-[15px]" />

Why it's bad: Creates maintenance burden, inconsistent spacing across app.

Correct approach:

tsx
// ✅ CORRECT: Use spacing scale
<Box className="p-3 m-6" />
<VStack space="lg" />

❌ Don't: Use className for Component Props

tsx
// ❌ INCORRECT: Using className instead of props
<VStack className="gap-4">
  <Box>Item</Box>
</VStack>

<Button className="bg-primary px-8 py-2">
  <ButtonText>Click</ButtonText>
</Button>

<Heading className="text-2xl font-bold">Title</Heading>

Why it's bad: Loses type safety, harder to maintain, bypasses design system.

Correct approach:

tsx
// ✅ CORRECT: Use component props
<VStack space="lg">
  <Box>Item</Box>
</VStack>

<Button variant="default" size="lg">
  <ButtonText>Click</ButtonText>
</Button>

<Heading size="2xl" bold>Title</Heading>

❌ Don't: Import from react-native for Wrapped Components

tsx
// ❌ INCORRECT: Direct React Native imports
import { KeyboardAvoidingView, View, Text } from 'react-native';

<KeyboardAvoidingView>
  <View>
    <Text>Content</Text>
  </View>
</KeyboardAvoidingView>

Why it's bad: Breaks cross-platform compatibility, loses theming and accessibility.

Correct approach:

tsx
// ✅ CORRECT: Use Gluestack wrappers
import { KeyboardAvoidingView } from '@/components/ui/keyboard-avoiding-view';
import { Box } from '@/components/ui/box';
import { Text } from '@/components/ui/text';

<KeyboardAvoidingView>
  <Box>
    <Text>Content</Text>
  </Box>
</KeyboardAvoidingView>

❌ Don't: Use ScrollView for Long Lists

tsx
// ❌ INCORRECT: ScrollView with map for long lists
<ScrollView>
  {items.map((item) => (
    <Box key={item.id}>
      <Text>{item.name}</Text>
    </Box>
  ))}
</ScrollView>

Why it's bad: No virtualization, all items rendered at once, poor performance.

Correct approach:

tsx
// ✅ CORRECT: Use FlatList for long lists
<FlatList
  data={items}
  renderItem={({ item }) => (
    <Box>
      <Text>{item.name}</Text>
    </Box>
  )}
  keyExtractor={(item) => item.id}
/>

❌ Don't: Use Animated API for Animations

tsx
// ❌ INCORRECT: Animated API (runs on JS thread)
import { Animated } from 'react-native';

const animValue = new Animated.Value(0);

Animated.timing(animValue, {
  toValue: 100,
  duration: 300,
}).start();

Why it's bad: Runs on JavaScript thread, can cause jank and dropped frames.

Correct approach:

tsx
// ✅ CORRECT: Use Reanimated (runs on UI thread)
import { useSharedValue, withTiming } from 'react-native-reanimated';

const animValue = useSharedValue(0);
animValue.value = withTiming(100, { duration: 300 });

Common Mistakes Summary

MistakeImpactCorrect Approach
Using React Native primitivesLoses theming, accessibility, cross-platform supportUse Gluestack components
Raw color valuesBreaks theming and dark modeUse semantic tokens
Skipping sub-componentsComponents won't render correctlyUse proper compound components
Inline styles for static valuesBypasses optimization, harder to maintainUse className
Arbitrary spacing valuesCreates maintenance burdenUse spacing scale
className instead of propsLoses type safetyUse component props when available
Direct react-native importsBreaks cross-platform compatibilityUse Gluestack wrappers
ScrollView for long listsPoor performanceUse FlatList
Animated APIJanky animationsUse Reanimated worklets

Critical Issues (Must Fix Immediately)

🔴 Critical: InputIcon Not Wrapped in InputSlot

tsx
// ❌ CRITICAL ERROR: Will break rendering
<Input>
  <InputIcon as={MailIcon} />
  <InputField />
</Input>

// ✅ MUST FIX: Wrap InputIcon in InputSlot
<Input>
  <InputSlot>
    <InputIcon as={MailIcon} />
  </InputSlot>
  <InputField />
</Input>

🔴 Critical: Missing ButtonText

tsx
// ❌ CRITICAL ERROR: Button won't render correctly
<Button>Submit</Button>

// ✅ MUST FIX: Use ButtonText
<Button>
  <ButtonText>Submit</ButtonText>
</Button>

🔴 Critical: Using React Native Instead of Gluestack

tsx
// ❌ CRITICAL ERROR: Breaks cross-platform support
import { View, Text } from 'react-native';

// ✅ MUST FIX: Use Gluestack components
import { Box } from '@/components/ui/box';
import { Text } from '@/components/ui/text';

Code Review Guidelines

High Priority (Must Have)

  1. Component consistency - All components use Gluestack wrappers
  2. Compound components - All sub-components used correctly
  3. Semantic tokens - No raw color values
  4. Component props - Props used instead of className when available

Medium Priority (Should Have)

  1. TypeScript types - All props and components typed
  2. Spacing scale - No arbitrary spacing values
  3. Performance - FlatList for long lists, memoization for expensive components
  4. Dark mode - Proper dark mode support

Low Priority (Nice to Have)

  1. Animations - Using Reanimated instead of Animated API
  2. Code organization - Logical component structure
  3. Documentation - Comments for complex logic

Escalation Guidance

When a design request cannot be satisfied with existing patterns:

Step 1: Push Back Early

Explain the implications:

  • Performance impact
  • Maintenance burden
  • Breaks theming/dark mode
  • Inconsistent with design system

Example:

"Using arbitrary spacing values like p-[13px] creates maintenance issues and breaks consistency. Can we use p-3 (12px) or p-4 (16px) from our spacing scale instead?"

Step 2: Propose Alternatives

Map to existing tokens:

tsx
// Request: "Make it slightly lighter red"
// ❌ Don't use: bg-red-400
// ✅ Propose: bg-destructive/80 (with alpha)

Suggest new semantic tokens:

tsx
// Request: "I need a success color"
// ✅ Propose: Add success token to design system
// Then use: text-success, bg-success

Step 3: Add to Design System

If truly needed, add token to gluestack-ui-provider/config.ts:

ts
// Add new semantic token
export const config = {
  tokens: {
    colors: {
      success: '#22c55e',
      'success-foreground': '#ffffff',
    },
  },
};

Step 4: Document Exception

If inline style is unavoidable, document why:

tsx
/**
 * Using inline style for dynamic safe area padding
 * Cannot use className as value comes from hook
 */
<Box style={{ paddingBottom: insets.bottom }}>
  {/* Content */}
</Box>

Quick Validation Script

Use this mental checklist when reviewing code:

  1. ✅ Gluestack components? (not React Native)
  2. ✅ Compound components correct? (ButtonText, InputSlot, etc.)
  3. ✅ Semantic tokens? (no raw colors)
  4. ✅ Component props? (space, size, variant)
  5. ✅ Spacing scale? (no arbitrary values)
  6. ✅ TypeScript types? (all props typed)
  7. ✅ Performance? (FlatList, memoization)
  8. ✅ Cross-platform? (Gluestack wrappers)

Reference