Component
Skill Shankulkarni/RN-Lib-Claude/plugins/rn-lib-claude/skills/component
Use when building React Native UI components for a library — Fabric-compatible, New Architecture patterns, ref forwarding, Platform.OS, accessibility, compound components, and safe rendering patterns.From its SKILL.md
npx -y skills add Shankulkarni/RN-Lib-Claude --skill componentAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.8 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
UI Component Patterns
New Architecture (Fabric) only. RN 0.76+.
CRITICAL — Rendering Safety
Never && with potentially falsy values
// ❌ Renders "0" as text node → crashes or visual bug
{count && <Badge count={count} />}
{items.length && <List items={items} />}
// ✅ Ternary with null, or explicit boolean
{count > 0 ? <Badge count={count} /> : null}
{!!items.length && <List items={items} />}
{Boolean(count) && <Badge count={count} />}
All strings must be inside <Text>
// ❌ Crashes — string outside Text in New Architecture
<View>Hello world</View>
<View>{user.name}</View>
// ✅
<View><Text>Hello world</Text></View>
<View><Text>{user.name}</Text></View>
Use Pressable — never TouchableOpacity/TouchableHighlight
// ❌ Legacy, deprecated
<TouchableOpacity onPress={onPress}>
<TouchableHighlight onPress={onPress}>
// ✅ Pressable — composable, New Architecture native
<Pressable
onPress={onPress}
style={({ pressed }) => [styles.button, pressed && styles.pressed]}
>
For animated press states with Reanimated — see animations skill (GestureDetector + Gesture.Tap()).
Component Template
import { forwardRef } from 'react'
import { Pressable, StyleSheet, type PressableProps } from 'react-native'
type MyButtonProps = Omit<PressableProps, 'style'> & {
variant?: 'primary' | 'secondary' | 'ghost'
size?: 'sm' | 'md' | 'lg'
style?: PressableProps['style']
}
const MyButton = forwardRef<View, MyButtonProps>(
({ variant = 'primary', size = 'md', style, children, ...props }, ref) => {
return (
<Pressable
ref={ref}
style={({ pressed }) => [
styles.base,
styles[variant],
styles[size],
pressed && styles.pressed,
typeof style === 'function' ? style({ pressed }) : style,
]}
accessibilityRole="button"
{...props}
>
{children}
</Pressable>
)
},
)
MyButton.displayName = 'MyButton'
const styles = StyleSheet.create({
base: { alignItems: 'center', justifyContent: 'center', borderRadius: 8, borderCurve: 'continuous' },
primary: { backgroundColor: '#000' },
secondary: { backgroundColor: '#f0f0f0' },
ghost: { backgroundColor: 'transparent' },
sm: { paddingVertical: 6, paddingHorizontal: 12, minHeight: 32 },
md: { paddingVertical: 10, paddingHorizontal: 16, minHeight: 44 },
lg: { paddingVertical: 14, paddingHorizontal: 20, minHeight: 52 },
pressed: { opacity: 0.7 },
})
export { MyButton }
export type { MyButtonProps }
Prop Design
// ✅ Accept color as prop — never hardcode
type ChipProps = {
color?: string // default to a semantic value, never '#ff0000'
backgroundColor?: string
}
// ✅ Accept size as prop — never hardcode dimensions
type AvatarProps = {
size?: number // default 48
}
// ✅ Extend native props — consumers can pass testID, onLayout, etc.
type CardProps = ViewProps & {
variant?: 'elevated' | 'outlined' | 'filled'
}
// ✅ Spread rest props always
const Card = forwardRef<View, CardProps>(({ variant = 'elevated', style, ...props }, ref) => (
<View ref={ref} style={[styles[variant], style]} {...props} />
))
Compound Components
Prefer compound components over boolean prop explosion.
// ❌ Boolean prop explosion
<Button icon={<Icon />} iconPosition="left" hasSpinner isLoading label="Save" />
// ✅ Compound — explicit, composable
<Button onPress={save}>
<ButtonIcon><SaveIcon /></ButtonIcon>
<ButtonText>Save</ButtonText>
<ButtonSpinner visible={isLoading} />
</Button>
Implementation:
type ButtonContextValue = { disabled: boolean }
const ButtonContext = createContext<ButtonContextValue>({ disabled: false })
const Button = ({ disabled = false, children, ...props }: ButtonProps) => (
<ButtonContext.Provider value={{ disabled }}>
<Pressable disabled={disabled} {...props}>{children}</Pressable>
</ButtonContext.Provider>
)
const ButtonText = ({ style, ...props }: TextProps) => {
const { disabled } = use(ButtonContext)
return <Text style={[styles.text, disabled && styles.textDisabled, style]} {...props} />
}
const ButtonIcon = ({ children }: { children: ReactNode }) => (
<View style={styles.icon}>{children}</View>
)
Button.Text = ButtonText
Button.Icon = ButtonIcon
Styling
const styles = StyleSheet.create({
// ✅ borderCurve: 'continuous' — smooth iOS squircle corners
card: { borderRadius: 12, borderCurve: 'continuous' },
// ✅ gap for sibling spacing, padding for internal
row: { flexDirection: 'row', gap: 8 },
card2: { padding: 16 },
// ✅ Platform shadows
shadow: Platform.select({
ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.08, shadowRadius: 8 },
android: { elevation: 3 },
default: {},
}),
})
Images
// ❌ React Native Image — no blurhash, no priority, no caching control
import { Image } from 'react-native'
// ✅ expo-image — progressive loading, blurhash, CDN caching, priority
import { Image } from 'expo-image'
<Image
source={{ uri: imageUri }}
placeholder={{ blurhash }}
contentFit="cover"
priority="high"
style={styles.image}
/>
Add to peer deps if library uses images:
"expo-image": ">=2.0.0"
Accessibility Checklist
-
accessibilityRoleon every interactive element (button,image,header, etc.) -
accessibilityLabelon icon-only buttons and images -
accessibilityState={{ disabled, selected, checked }}when state changes -
accessibilityHintfor non-obvious interactions - Minimum touch target 44×44pt (
minHeight: 44, minWidth: 44) -
importantForAccessibility="no"on decorative elements
collapsable={false} for Native Component Children
Fabric may flatten layout-only views. If a component is a child of a native view that expects a specific child count, add:
// Prevents Fabric from removing this view as a layout optimization
<View collapsable={false} style={styles.wrapper}>
{children}
</View>
Use sparingly — only when native code explicitly references this view.
Export Rules
// src/index.ts — export component + all types
export { Button, ButtonText, ButtonIcon } from './components/Button'
export type { ButtonProps } from './components/Button'
displayNamerequired on everyforwardRefandmemocomponent- Spread
...propsso consumers can passtestID,onLayout,style - No hardcoded colors, dimensions, or font sizes — always accept as props
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.