Claude
Skills
Sign in
Back

gluestack-ui-v4:variants

Included with Lifetime
$97 forever

Guide for creating custom variants for gluestack-ui v4 components - covers tva usage, extending components, variant patterns, and customization strategies.

Design

What this skill does


# Gluestack UI v4 - Creating Component Variants

This sub-skill focuses on creating custom variants for existing gluestack-ui v4 components, allowing you to extend the design system with project-specific styling patterns while maintaining consistency and type safety.

## When to Create a Variant

Create a new variant when:

1. **Repeating the same style combination** - Multiple places use the same className pattern
2. **Project-specific design patterns** - Brand-specific button styles, card types, etc.
3. **Conditional styling** - Component appearance changes based on props
4. **Extending existing components** - Adding new visual styles to Gluestack components
5. **Theme-specific variations** - Different appearances for specific contexts

**Don't create variants for:**
- One-off custom styles (use className instead)
- Simple modifications (use existing props + className)
- Styles that should be in the global design system

## Variant Creation Workflow

### Step 1: Analyze the Component

Before creating a variant, understand:

1. **What's the base component?** - Button, Card, Badge, etc.
2. **What visual states are needed?** - Colors, sizes, borders, shadows
3. **Are there sub-components?** - ButtonText, CardHeader, etc.
4. **What props should control variants?** - variant, size, state props
5. **Should variants affect children?** - Parent variants for sub-components

### Step 2: Plan Variant Structure

Define your variant system:

```tsx
// Example: Planning a Badge component with variants
{
  variant: ['default', 'success', 'warning', 'error', 'info']
  size: ['sm', 'md', 'lg']
  shape: ['rounded', 'pill', 'square']
}
```

### Step 3: Implement with tva

Use `tva` (Tailwind Variant Authority) to create type-safe, composable variants.

## Creating Simple Variants

### Template: Adding Variants to a Custom Component

```tsx
import React from 'react';
import { tva } from '@gluestack-ui/utils/nativewind-utils';
import { Box } from '@/components/ui/box';
import { Text } from '@/components/ui/text';

interface BadgeProps {
  readonly variant?: 'default' | 'success' | 'warning' | 'error' | 'info';
  readonly size?: 'sm' | 'md' | 'lg';
  readonly shape?: 'rounded' | 'pill' | 'square';
  readonly className?: string;
  readonly children: React.ReactNode;
}

// Define variant styles
const badgeStyles = tva({
  base: 'inline-flex items-center justify-center font-medium',
  variants: {
    variant: {
      default: 'bg-muted text-muted-foreground',
      success: 'bg-primary/10 text-primary border border-primary/20',
      warning: 'bg-accent/10 text-accent-foreground border border-accent/20',
      error: 'bg-destructive/10 text-destructive border border-destructive/20',
      info: 'bg-secondary/10 text-secondary-foreground border border-secondary/20',
    },
    size: {
      sm: 'px-2 py-0.5 text-xs',
      md: 'px-3 py-1 text-sm',
      lg: 'px-4 py-1.5 text-base',
    },
    shape: {
      rounded: 'rounded-md',
      pill: 'rounded-full',
      square: 'rounded-none',
    },
  },
  defaultVariants: {
    variant: 'default',
    size: 'md',
    shape: 'rounded',
  },
});

export const Badge = ({
  variant,
  size,
  shape,
  className,
  children
}: BadgeProps) => {
  return (
    <Box className={badgeStyles({ variant, size, shape, class: className })}>
      <Text>{children}</Text>
    </Box>
  );
};

// Usage:
// <Badge variant="success" size="lg" shape="pill">Active</Badge>
// <Badge variant="error" size="sm">Error</Badge>
```

**Key Points:**
- ✅ Uses `tva` for variant management
- ✅ Base styles apply to all variants
- ✅ Multiple variant dimensions (variant, size, shape)
- ✅ Default variants specified
- ✅ className override support with `class` parameter
- ✅ TypeScript types for variant options

## Extending Existing Gluestack Components

### Template: Adding Custom Variants to Button

```tsx
import React from 'react';
import { tva } from '@gluestack-ui/utils/nativewind-utils';
import { Button as GluestackButton, ButtonText } from '@/components/ui/button';

// Define additional variant styles
const customButtonStyles = tva({
  base: '',
  variants: {
    variant: {
      // Extend existing variants with new ones
      gradient: 'bg-gradient-to-r from-primary to-accent',
      glass: 'bg-background/20 backdrop-blur-lg border border-border/50',
      neon: 'bg-transparent border-2 border-primary shadow-[0_0_15px_rgba(59,130,246,0.5)]',
    },
    size: {
      // Add custom sizes
      xs: 'px-2 py-1',
      xl: 'px-8 py-4',
    },
  },
});

interface CustomButtonProps {
  readonly variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link' | 'gradient' | 'glass' | 'neon';
  readonly size?: 'default' | 'sm' | 'lg' | 'icon' | 'xs' | 'xl';
  readonly className?: string;
  readonly onPress?: () => void;
  readonly isDisabled?: boolean;
  readonly children: React.ReactNode;
}

export const CustomButton = ({
  variant = 'default',
  size = 'default',
  className,
  onPress,
  isDisabled,
  children,
}: CustomButtonProps) => {
  // Use Gluestack Button for built-in variants
  if (['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'].includes(variant)) {
    return (
      <GluestackButton
        variant={variant as any}
        size={['default', 'sm', 'lg', 'icon'].includes(size) ? size as any : 'default'}
        onPress={onPress}
        isDisabled={isDisabled}
        className={className}
      >
        {children}
      </GluestackButton>
    );
  }

  // Use custom variants
  return (
    <GluestackButton
      onPress={onPress}
      isDisabled={isDisabled}
      className={customButtonStyles({ variant: variant as any, size: size as any, class: className })}
    >
      {children}
    </GluestackButton>
  );
};

// Usage:
// <CustomButton variant="gradient" size="xl">
//   <ButtonText>Gradient Button</ButtonText>
// </CustomButton>
//
// <CustomButton variant="neon" size="lg">
//   <ButtonText>Neon Button</ButtonText>
// </CustomButton>
```

**Key Points:**
- ✅ Extends existing component
- ✅ Preserves original variants
- ✅ Adds new custom variants
- ✅ Maintains compound component pattern
- ✅ Type-safe variant options

## Parent-Child Variant Relationships

When creating components with sub-components, use `parentVariants` to style children based on parent state.

### Template: Card with Variant-Aware Children

```tsx
import React from 'react';
import { tva } from '@gluestack-ui/utils/nativewind-utils';
import { Box } from '@/components/ui/box';
import { Heading } from '@/components/ui/heading';
import { Text } from '@/components/ui/text';

interface CardProps {
  readonly variant?: 'default' | 'elevated' | 'outlined' | 'ghost';
  readonly colorScheme?: 'neutral' | 'primary' | 'success' | 'error';
  readonly className?: string;
  readonly children: React.ReactNode;
}

interface CardHeaderProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

interface CardBodyProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

// Parent card styles
const cardStyles = tva({
  base: 'rounded-lg overflow-hidden',
  variants: {
    variant: {
      default: 'border border-border bg-card',
      elevated: 'shadow-hard-2 bg-card',
      outlined: 'border-2 border-border bg-transparent',
      ghost: 'bg-transparent',
    },
    colorScheme: {
      neutral: '',
      primary: 'border-primary/20',
      success: 'border-primary/20',
      error: 'border-destructive/20',
    },
  },
  compoundVariants: [
    {
      variant: 'default',
      colorScheme: 'primary',
      class: 'bg-primary/5',
    },
    {
      variant: 'default',
      colorScheme: 'success',
      class: 'bg-primary/5',
    },
    {
      variant: 'default',
      colorScheme: 'error',
      class: 'bg-destructive/5',
    },
  ],
  defaultVariants: {
    variant: 'default',
    colorScheme: 'neutral',
  },
});

// Child styles that respond to parent variants
const cardHeaderStyles = tva({
  base: 'p-4 border-b',
  pare

Related in Design