Install
$ agentstack add skill-gluestack-agent-skills-creating-components ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
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:
- What is the component's purpose?
- Form input, data display, navigation, layout, etc.
- Which Gluestack components do I need?
- Check official docs:
https://gluestack.io/ui/docs/components/${componentName}/ - Use Gluestack wrappers, not React Native primitives
- Does it need compound components?
- Multiple related sub-components (Header, Body, Footer)
- Icon + Text combinations
- Label + Input patterns
- What props should it accept?
- Size variants:
sm,md,lg - Visual variants:
default,outline,ghost - State props:
isDisabled,isInvalid,isLoading - Custom className for overrides
- 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
readonlyprops - ✅ 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 colortext-muted-foreground- Muted/secondary texttext-card-foreground- Text on card backgroundstext-primary,text-primary-foreground- Primary brand colorstext-secondary,text-secondary-foreground- Secondary colorstext-destructive- Error statestext-accent,text-accent-foreground- Accent colors- With alpha:
text-foreground/70,text-primary/90
Background Colors:
bg-background- Main backgroundbg-card- Card backgroundsbg-muted- Muted backgroundsbg-popover- Popover/modal backgroundsbg-primary,bg-secondary,bg-destructive,bg-accent- Action colors- With alpha:
bg-primary/10,bg-muted/50
Border Colors:
border-border- Standard bordersborder-input- Input bordersring-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
readonlyprops - [ ] 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.
- Author: gluestack
- Source: gluestack/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.