AgentStack
SKILL verified MIT Self-run

Gluestack Ui V5:creating Components

skill-gluestack-agent-skills-creating-components · by gluestack

Step-by-step guide for creating components with gluestack-ui v5 (Tailwind v4 CSS-first) - covers planning, structure, styling, TypeScript, and common component patterns.

No reviews yet
0 installs
2 views
0.0% view→install

Install

$ agentstack add skill-gluestack-agent-skills-creating-components

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Gluestack Ui V5:creating Components? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Gluestack UI v5 — Creating Components

This sub-skill provides practical guidance for creating new components using gluestack-ui v5 (Tailwind v4 CSS-first), from planning to implementation.

Component Creation Workflow

Step 1: Plan Component Structure

Before writing code, answer these questions:

  1. What is the component's purpose?
  • Form input, data display, navigation, layout, etc.
  1. Which Gluestack components do I need?
  • Check official docs: https://gluestack.io/ui/docs/components/${componentName}/
  • Use Gluestack wrappers, not React Native primitives
  1. Does it need compound components?
  • Multiple related sub-components (Header, Body, Footer)
  • Icon + Text combinations
  • Label + Input patterns
  1. What props should it accept?
  • Size variants: sm, md, lg
  • Visual variants: default, outline, ghost
  • State props: isDisabled, isInvalid, isLoading
  • Custom className for overrides
  1. Does it need variants?
  • If yes, use tva (Tailwind Variant Authority)
  • Define base styles and variant options

Step 2: Check Official Documentation

ALWAYS verify component usage before creating:

# Visit official docs for the component
https://gluestack.io/ui/docs/components/${componentName}/

Check for:

  • Latest API and props
  • Required sub-components
  • Usage examples
  • Accessibility features

Step 3: Create Component File

Follow this file structure:

components/
├── ui/                      # Gluestack UI components (copy-paste)
│   ├── box/
│   ├── button/
│   └── input/
└── custom/                  # Your custom components
    ├── profile-card/
    │   └── index.tsx
    └── login-form/
        └── index.tsx

Component Templates

Template 1: Simple Component (No Variants)

Use when component has consistent styling without variants.

import React from 'react';
import { Box } from '@/components/ui/box';
import { Text } from '@/components/ui/text';
import { Heading } from '@/components/ui/heading';

interface ProfileCardProps {
  readonly name: string;
  readonly email: string;
  readonly className?: string;
}

export const ProfileCard = ({ name, email, className }: ProfileCardProps) => {
  return (
    
      
        {name}
      
      
        {email}
      
    
  );
};

Key points:

  • ✅ Uses Gluestack components (Box, Text, Heading)
  • ✅ TypeScript interface with readonly props
  • ✅ Semantic tokens (bg-card, text-card-foreground)
  • ✅ Accepts className for customization
  • ✅ Component props for sizing (Heading size)

Template 2: Component with Variants (Using tva)

Use when component needs multiple visual styles or sizes.

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 AlertProps {
  readonly variant?: 'default' | 'success' | 'warning' | 'destructive';
  readonly size?: 'sm' | 'md' | 'lg';
  readonly className?: string;
  readonly children: React.ReactNode;
}

const alertStyles = tva({
  base: 'rounded-lg border p-4',
  variants: {
    variant: {
      default: 'bg-card border-border',
      success: 'bg-primary/10 border-primary',
      warning: 'bg-accent/10 border-accent',
      destructive: 'bg-destructive/10 border-destructive',
    },
    size: {
      sm: 'p-2',
      md: 'p-4',
      lg: 'p-6',
    },
  },
  defaultVariants: {
    variant: 'default',
    size: 'md',
  },
});

const alertTextStyles = tva({
  base: 'font-sans',
  parentVariants: {
    variant: {
      default: 'text-foreground',
      success: 'text-primary',
      warning: 'text-accent-foreground',
      destructive: 'text-destructive',
    },
    size: {
      sm: 'text-xs',
      md: 'text-sm',
      lg: 'text-base',
    },
  },
});

export const Alert = ({ variant, size, className, children }: AlertProps) => {
  return (
    
      
        {children}
      
    
  );
};

Key points:

  • ✅ Uses tva for variant management
  • ✅ Base styles + variant options
  • ✅ Default variants specified
  • ✅ Parent variants for child components
  • ✅ className override support

Template 3: Compound Component Pattern

Use when component has multiple related sub-components.

import React from 'react';
import { Box } from '@/components/ui/box';
import { Heading } from '@/components/ui/heading';
import { Text } from '@/components/ui/text';
import { HStack } from '@/components/ui/hstack';

// Main Card Component
interface CardProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

export const Card = ({ className, children }: CardProps) => {
  return (
    
      {children}
    
  );
};

// Card Header Sub-component
interface CardHeaderProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

export const CardHeader = ({ className, children }: CardHeaderProps) => {
  return (
    
      {children}
    
  );
};

// Card Body Sub-component
interface CardBodyProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

export const CardBody = ({ className, children }: CardBodyProps) => {
  return (
    
      {children}
    
  );
};

// Card Footer Sub-component
interface CardFooterProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

export const CardFooter = ({ className, children }: CardFooterProps) => {
  return (
    
      {children}
    
  );
};

// Usage
// 
//   
//     Title
//   
//   
//     Content
//   
//   
//     Action
//   
// 

Key points:

  • ✅ Main component + sub-components
  • ✅ Each sub-component is independent
  • ✅ Consistent styling across sub-components
  • ✅ Flexible composition

Template 4: Form Component

Use for form inputs with labels, validation, and error messages.

import React, { useState } from 'react';
import { FormControl, FormControlLabel, FormControlLabelText } from '@/components/ui/form-control';
import { FormControlError, FormControlErrorIcon, FormControlErrorText } from '@/components/ui/form-control';
import { FormControlHelper, FormControlHelperText } from '@/components/ui/form-control';
import { Input, InputField, InputSlot, InputIcon } from '@/components/ui/input';
import { MailIcon, AlertCircleIcon } from '@/components/ui/icon';

interface EmailInputProps {
  readonly label?: string;
  readonly placeholder?: string;
  readonly helperText?: string;
  readonly value: string;
  readonly error?: string;
  readonly onChange: (value: string) => void;
  readonly className?: string;
}

export const EmailInput = ({
  label = 'Email Address',
  placeholder = 'Enter your email',
  helperText,
  value,
  error,
  onChange,
  className,
}: EmailInputProps) => {
  const [isFocused, setIsFocused] = useState(false);

  return (
    
      
        {label}
      

      
        
          
        
         setIsFocused(true)}
          onBlur={() => setIsFocused(false)}
          keyboardType="email-address"
          autoCapitalize="none"
        />
      

      {error && (
        
          
          {error}
        
      )}

      {helperText && !error && (
        
          {helperText}
        
      )}
    
  );
};

Key points:

  • ✅ FormControl wrapper for validation
  • ✅ InputIcon wrapped in InputSlot (CRITICAL)
  • ✅ Error and helper text handling
  • ✅ Focus state management
  • ✅ Proper keyboard type

Template 5: Interactive Component with State

Use for components with internal state and interactions.

import React, { useState } from 'react';
import { Box } from '@/components/ui/box';
import { Text } from '@/components/ui/text';
import { Pressable } from '@/components/ui/pressable';
import { ChevronDownIcon, ChevronUpIcon } from '@/components/ui/icon';
import { Icon } from '@/components/ui/icon';
import { VStack } from '@/components/ui/vstack';
import { HStack } from '@/components/ui/hstack';

interface AccordionProps {
  readonly title: string;
  readonly children: React.ReactNode;
  readonly defaultExpanded?: boolean;
  readonly className?: string;
}

export const Accordion = ({
  title,
  children,
  defaultExpanded = false,
  className,
}: AccordionProps) => {
  const [isExpanded, setIsExpanded] = useState(defaultExpanded);

  return (
    
       setIsExpanded(!isExpanded)}>
        
          
            {title}
          
          
        
      

      {isExpanded && (
        
          {children}
        
      )}
    
  );
};

Key points:

  • ✅ Internal state management
  • ✅ Conditional rendering
  • ✅ Interactive elements (Pressable)
  • ✅ Icon state changes
  • ✅ Layout with space prop

Template 6: Component with Loading State

Use for components that fetch data or perform async operations.

import React from 'react';
import { Button, ButtonText, ButtonSpinner, ButtonIcon } from '@/components/ui/button';
import { CheckIcon } from '@/components/ui/icon';

interface SubmitButtonProps {
  readonly isLoading?: boolean;
  readonly isSuccess?: boolean;
  readonly onPress: () => void;
  readonly className?: string;
}

export const SubmitButton = ({
  isLoading = false,
  isSuccess = false,
  onPress,
  className,
}: SubmitButtonProps) => {
  return (
    
      {isLoading && }
      {isSuccess && }
      
        {isLoading ? 'Submitting...' : isSuccess ? 'Success!' : 'Submit'}
      
    
  );
};

Key points:

  • ✅ Loading state with ButtonSpinner
  • ✅ Success state with icon
  • ✅ Disabled during loading
  • ✅ Dynamic text based on state

Common Component Recipes

Recipe 1: Profile Card with Avatar

import React from 'react';
import { Box } from '@/components/ui/box';
import { HStack } from '@/components/ui/hstack';
import { VStack } from '@/components/ui/vstack';
import { Text } from '@/components/ui/text';
import { Heading } from '@/components/ui/heading';
import { Image } from '@/components/ui/image';

interface ProfileCardProps {
  readonly name: string;
  readonly email: string;
  readonly avatarUrl: string;
  readonly bio?: string;
  readonly className?: string;
}

export const ProfileCard = ({
  name,
  email,
  avatarUrl,
  bio,
  className,
}: ProfileCardProps) => {
  return (
    
      
        
        
          
            {name}
          
          
            {email}
          
          {bio && (
            
              {bio}
            
          )}
        
      
    
  );
};

Recipe 2: Status Badge

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 StatusBadgeProps {
  readonly status: 'active' | 'inactive' | 'pending' | 'error';
  readonly className?: string;
}

const badgeStyles = tva({
  base: 'rounded-full px-3 py-1 inline-flex',
  variants: {
    status: {
      active: 'bg-primary/10',
      inactive: 'bg-muted',
      pending: 'bg-accent/10',
      error: 'bg-destructive/10',
    },
  },
});

const badgeTextStyles = tva({
  base: 'text-xs font-medium',
  parentVariants: {
    status: {
      active: 'text-primary',
      inactive: 'text-muted-foreground',
      pending: 'text-accent-foreground',
      error: 'text-destructive',
    },
  },
});

export const StatusBadge = ({ status, className }: StatusBadgeProps) => {
  const labels = {
    active: 'Active',
    inactive: 'Inactive',
    pending: 'Pending',
    error: 'Error',
  };

  return (
    
      
        {labels[status]}
      
    
  );
};

Recipe 3: Search Input

import React from 'react';
import { Input, InputField, InputSlot, InputIcon } from '@/components/ui/input';
import { SearchIcon, XIcon } from '@/components/ui/icon';
import { Pressable } from '@/components/ui/pressable';

interface SearchInputProps {
  readonly value: string;
  readonly onChange: (value: string) => void;
  readonly placeholder?: string;
  readonly onClear?: () => void;
  readonly className?: string;
}

export const SearchInput = ({
  value,
  onChange,
  placeholder = 'Search...',
  onClear,
  className,
}: SearchInputProps) => {
  const handleClear = () => {
    onChange('');
    onClear?.();
  };

  return (
    
      
        
      
      
      {value.length > 0 && (
        
          
        
      )}
    
  );
};

Recipe 4: List Item with Action

import React from 'react';
import { Pressable } from '@/components/ui/pressable';
import { HStack } from '@/components/ui/hstack';
import { VStack } from '@/components/ui/vstack';
import { Box } from '@/components/ui/box';
import { Text } from '@/components/ui/text';
import { Icon } from '@/components/ui/icon';
import { ChevronRightIcon } from '@/components/ui/icon';

interface ListItemProps {
  readonly title: string;
  readonly description?: string;
  readonly onPress: () => void;
  readonly showChevron?: boolean;
  readonly className?: string;
}

export const ListItem = ({
  title,
  description,
  onPress,
  showChevron = true,
  className,
}: ListItemProps) => {
  return (
    
      
        
          
            
              {title}
            
            {description && (
              
                {description}
              
            )}
          
          {showChevron && (
            
          )}
        
      
    
  );
};

CRITICAL: Semantic Token Requirements

Before creating any component, understand these STRICT token requirements:

✅ ALLOWED Tokens (Semantic Only)

Use ONLY these semantic token patterns:

Text Colors:

  • text-foreground - Main text color
  • text-muted-foreground - Muted/secondary text
  • text-card-foreground - Text on card backgrounds
  • text-primary, text-primary-foreground - Primary brand colors
  • text-secondary, text-secondary-foreground - Secondary colors
  • text-destructive - Error states
  • text-accent, text-accent-foreground - Accent colors
  • With alpha: text-foreground/70, text-primary/90

Background Colors:

  • bg-background - Main background
  • bg-card - Card backgrounds
  • bg-muted - Muted backgrounds
  • bg-popover - Popover/modal backgrounds
  • bg-primary, bg-secondary, bg-destructive, bg-accent - Action colors
  • With alpha: bg-primary/10, bg-muted/50

Border Colors:

  • border-border - Standard borders
  • border-input - Input borders
  • ring-ring - Focus rings
  • With alpha: border-border/50, border-primary/20

❌ PROHIBITED Tokens (NEVER Use These)

NEVER use these token patterns - they are STRICTLY PROHIBITED:

// ❌ PROHIBITED: Generic typography tokens
text-typography-900
text-typography-700
text-typography-500

// ❌ PROHIBITED: Neutral color tokens
bg-neutral-100
text-neutral-600
border-neutral-300

// ❌ PROHIBITED: Gray/Slate color scales
bg-gray-50
text-gray-900
border-gray-200
text-slate-700

// ❌ PROHIBITED: Numbered color tokens
bg-blue-600
text-red-500
border-green-400
bg-indigo-500

// ❌ PROHIBITED: Arbitrary values
bg-[#3b82f6]
text-[#DC2626]

// ❌ PROHIBITED: Opacity utilities
opacity-70
bg-opacity-90
text-opacity-80

Why This Matters

Using prohibited tokens will:

  • ❌ Break dark mode
  • ❌ Violate design system
  • ❌ Create maintenance debt
  • ❌ Fail code review

Using semantic tokens will:

  • ✅ Work in light AND dark mode
  • ✅ Match design system
  • ✅ Be maintainable
  • ✅ Express intent clearly

Best Practices Checklist

When creating a component, verify:

Structure

  • [ ] Uses Gluestack components (not React Native primitives)
  • [ ] Imports from @/components/ui/*
  • [ ] Follows compound component pattern when needed
  • [ ] InputIcon wrapped in InputSlot (if using Input)

TypeScript

  • [ ] Interface defined with readonly props
  • [ ] All props typed correctly
  • [ ] Optional props have ? marker
  • [ ] Default values specified in function params

Styling

  • [ ] CRITICAL: Uses ONLY semantic tokens - NO typography-*, neutral-*, gray-*, slate-*, or numbered colors (red-500, `blue-60

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.