with one click
create-component
Scaffold a UI component with unistyles variants pattern
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Scaffold a UI component with unistyles variants pattern
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Transform the base template into a specific application
Add or update i18n keys in both English and Arabic locale files
Scaffold a complete feature module with types, service, hooks, schema, and constants
Generate a validated form with Zod schema, react-hook-form, and i18n
Add a new screen or tab to the app with routing and i18n
Review changed code against project conventions and rules
| name | create-component |
| description | Scaffold a UI component with unistyles variants pattern |
| auto-invocable | true |
| triggers | ["create a component","add a UI component","new component","scaffold component"] |
Scaffold a UI component following the project's exact unistyles variant pattern.
Ask the user for:
src/common/components/) or feature-specific (src/features/<feature>/components/)<ComponentName>/
├── <ComponentName>.tsx
├── <ComponentName>.types.ts
├── <ComponentName>.styles.ts
└── index.ts
// <ComponentName>.types.ts
import type { ViewProps } from 'react-native';
import type { <ComponentName>StyleVariants } from './<ComponentName>.styles';
export interface <ComponentName>Props extends ViewProps, <ComponentName>StyleVariants {
// Required props
// Optional props with defaults documented
}
Type rules:
ViewProps, PressableProps, etc.).styles.ts fileimport type for all type imports@/common/components/shared.types (e.g., ComponentSize)Follow the exact pattern from Button.styles.ts:
// <ComponentName>.styles.ts
import { StyleSheet, type UnistylesVariants } from 'react-native-unistyles';
export const styles = StyleSheet.create((theme) => ({
container: {
// Base styles using theme tokens
backgroundColor: theme.colors.background.surface,
borderRadius: theme.metrics.borderRadius.md,
padding: theme.metrics.spacing.p16,
// Variants block for prop-driven style changes
variants: {
variant: {
primary: { /* ... */ },
secondary: { /* ... */ },
},
size: {
sm: { padding: theme.metrics.spacing.p8 },
md: { padding: theme.metrics.spacing.p12 },
lg: { padding: theme.metrics.spacing.p16 },
},
disabled: {
true: { opacity: 0.5 },
},
},
},
// Additional style targets with their own variants if needed
}));
export type <ComponentName>StyleVariants = UnistylesVariants<typeof styles>;
Style rules:
StyleSheet from react-native-unistylesStyleSheet.create((theme) => ({...}))theme.colors.*theme.metrics.spacing.* or theme.metrics.spacingV.*variants block for any prop-driven style changestrue/false keys) for toggleable states<ComponentName>StyleVariants// <ComponentName>.tsx
import { View } from 'react-native';
import { Text } from '@/common/components/Text';
import { styles } from './<ComponentName>.styles';
import type { <ComponentName>Props } from './<ComponentName>.types';
export function <ComponentName>({
// Destructure props with defaults
variant = 'primary',
size = 'md',
disabled = false,
style,
...rest
}: <ComponentName>Props) {
// Call useVariants ONCE before accessing any styles
styles.useVariants({ variant, size, disabled });
return (
<View
style={[styles.container, style]}
accessibilityRole="..." // appropriate role
{...rest}
>
{/* Component content */}
</View>
);
}
If interactive (handles press):
import { Pressable } from 'react-native';
import Animated from 'react-native-reanimated';
import { useAnimatedPress } from '@/hooks/useAnimatedPress';
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
export function <ComponentName>({ onPress, disabled, ...props }: <ComponentName>Props) {
const { animatedStyle, onPressIn, onPressOut } = useAnimatedPress();
styles.useVariants({ disabled });
return (
<AnimatedPressable
style={[styles.container, animatedStyle, props.style]}
onPress={onPress}
onPressIn={onPressIn}
onPressOut={onPressOut}
disabled={disabled}
accessibilityRole="button"
accessibilityLabel={/* i18n label */}
accessibilityState={{ disabled }}
{...props}
>
{/* Content */}
</AnimatedPressable>
);
}
Component rules:
styles.useVariants(...) once per render, before accessing stylesaccessibilityRole and accessibilityLabelaccessibilityState for disabled, busy, selected statesText from @/common/components/Text, NOT from react-nativeuseTranslation() hook// index.ts
export { <ComponentName> } from './<ComponentName>';
export type { <ComponentName>Props } from './<ComponentName>.types';
If the component is in src/common/components/, add it to src/common/components/index.ts in the appropriate category section:
// Add under the matching category comment
export { <ComponentName> } from './<ComponentName>';
export type { <ComponentName>Props } from './<ComponentName>';
Run npm run validate to ensure no TypeScript or lint errors.
StyleSheet from react-native-unistyles, NOT react-nativeText from @/common/components/Text, NOT react-nativestyles.useVariants() called once before any style access@/ path aliases