Install
$ agentstack add skill-gluestack-agent-skills-performance ✓ 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 — Performance & Cross-Platform
This sub-skill focuses on performance optimization, cross-platform compatibility, and React Native best practices for gluestack-ui v5 (NativeWind v5 / UniWind).
Rule 12: Cross-Platform Rendering (Native & Web)
Gluestack UI v5 components are designed to work seamlessly on both React Native (iOS/Android) and Web platforms. Always use Gluestack wrapper components instead of direct React Native imports to ensure cross-platform compatibility.
Critical Rule: Always Use Gluestack Wrappers
NEVER import components directly from react-native when a Gluestack wrapper exists. Gluestack wrappers handle platform-specific differences automatically.
Platform-Specific Component Mapping
| React Native Import | Gluestack Wrapper | Notes | |---------------------|-------------------|-------| | KeyboardAvoidingView from react-native | KeyboardAvoidingView from @/components/ui/keyboard-avoiding-view | Required for web compatibility | | Platform from react-native | Use only when absolutely necessary | Prefer Gluestack's built-in platform handling | | View, Text, etc. | Box, Text from @/components/ui/* | Always use Gluestack components |
Correct Pattern: Cross-Platform Components
// ✅ CORRECT: Using Gluestack KeyboardAvoidingView wrapper
import { KeyboardAvoidingView } from '@/components/ui/keyboard-avoiding-view';
import { Platform } from 'react-native'; // Only when needed for platform-specific logic
{/* Content */}
Incorrect Pattern: Direct React Native Imports
// ❌ INCORRECT: Direct import from react-native
import { KeyboardAvoidingView, Platform } from 'react-native';
{/* This may not work correctly on web */}
Web-Specific Considerations
- KeyboardAvoidingView: The Gluestack wrapper handles web gracefully (web doesn't need keyboard avoidance)
- SafeAreaView: Works on both native and web (web treats it as a regular View)
- ScrollView: Works identically on both platforms
- Platform.select: Only use when absolutely necessary; prefer Gluestack's built-in handling
Testing Cross-Platform Compatibility
Always test components on both platforms:
- Native: Run
npm run iosornpm run android - Web: Run
npm run weband verify in browser - Verify: Check that all components render correctly and interactions work on both platforms
Platform-Specific Code (When Necessary)
If you must use platform-specific code, use it sparingly and document why:
// Acceptable: Platform-specific behavior when Gluestack doesn't cover it
import { Platform } from 'react-native';
const keyboardBehavior = Platform.OS === 'ios' ? 'padding' : 'height';
{/* Content */}
Common Cross-Platform Issues to Avoid
- Direct React Native imports - Always use Gluestack wrappers
- Platform-specific styling without fallbacks - Ensure web has equivalent styles
- Native-only APIs - Check if web alternatives exist
- Missing web polyfills - Gluestack handles most of these automatically
Verification Checklist for Cross-Platform
- [ ] All components imported from
@/components/ui/*wrappers - [ ] No direct imports from
react-nativefor wrapped components - [ ] KeyboardAvoidingView uses Gluestack wrapper
- [ ] Tested on both native (iOS/Android) and web platforms
- [ ] All interactions work on both platforms
- [ ] Styling renders correctly on both platforms
- [ ] No platform-specific code without documentation
Rule 13: Performance & Best Practices
Follow these best practices to ensure optimal performance, type safety, and maintainability in React Native/Expo applications.
Use TypeScript
Define navigation and prop types for type safety. This catches errors at compile time and improves developer experience.
Correct Pattern
// ✅ CORRECT: Typed component props
interface LoginFormProps {
readonly onSubmit: (email: string, password: string) => void;
readonly isLoading?: boolean;
}
const LoginForm = ({ onSubmit, isLoading = false }: LoginFormProps) => {
// Component implementation
};
// ✅ CORRECT: Typed navigation
import { useRouter } from 'expo-router';
const router = useRouter();
router.push('/login' as any); // Type-safe navigation
Incorrect Pattern
// ❌ INCORRECT: No type definitions
const LoginForm = ({ onSubmit, isLoading }) => {
// No type safety
};
Memoize Components
Use React.memo and useCallback to prevent unnecessary rerenders, especially for expensive components or frequently re-rendered parent components.
Correct Pattern
// ✅ CORRECT: Memoized component
import React, { useCallback, useState } from 'react';
const ExpensiveComponent = React.memo(({ data, onUpdate }: Props) => {
// Expensive rendering logic
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
// Memoized callback prevents child rerenders
const handleUpdate = useCallback((value: string) => {
// Update logic
}, []);
return (
<>
setCount(count + 1)}>
Count: {count}
);
};
When to Memoize
- Components that receive stable props but parent rerenders frequently
- Callbacks passed to child components
- Expensive computations (use
useMemo)
Run Animations on UI Thread
Use Reanimated worklets for 60fps animations. This keeps animations smooth by running on the native UI thread instead of the JavaScript thread.
Correct Pattern
// ✅ CORRECT: Using Reanimated worklets
import { useSharedValue, withTiming } from 'react-native-reanimated';
import Animated from 'react-native-reanimated';
const AnimatedBox = Animated.createAnimatedComponent(Box);
const Component = () => {
const translateX = useSharedValue(0);
const handlePress = () => {
// Animation runs on UI thread
translateX.value = withTiming(100, { duration: 300 });
};
return (
Animate
);
};
Incorrect Pattern
// ❌ INCORRECT: Using Animated API (runs on JS thread)
import { Animated } from 'react-native';
const Component = () => {
const translateX = new Animated.Value(0);
// This runs on JavaScript thread, can cause jank
};
Handle Safe Areas
Use SafeAreaView or useSafeAreaInsets to handle device notches, status bars, and home indicators properly.
Correct Pattern
// ✅ CORRECT: Using SafeAreaView
import { SafeAreaView } from '@/components/ui/safe-area-view';
const Screen = () => (
{/* Content */}
);
// ✅ CORRECT: Using useSafeAreaInsets for custom layouts
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const CustomLayout = () => {
const insets = useSafeAreaInsets();
return (
{/* Content */}
);
};
Test on Real Devices
Simulator/emulator performance differs from real devices. Always test on physical devices before releasing.
Testing Checklist
- [ ] Test on real iOS device (iPhone/iPad)
- [ ] Test on real Android device
- [ ] Test on different screen sizes
- [ ] Test with different OS versions
- [ ] Test performance under load
- [ ] Test with slow network conditions
Use FlatList for Lists
Never use ScrollView with map for long lists. FlatList provides virtualization, which only renders visible items.
Correct Pattern
// ✅ CORRECT: Using FlatList
import { FlatList } from '@/components/ui/flat-list';
const ItemList = ({ items }: { items: Item[] }) => (
(
{item.name}
)}
keyExtractor={(item) => item.id}
ListEmptyComponent={No items found}
/>
);
Incorrect Pattern
// ❌ INCORRECT: Using ScrollView with map (no virtualization)
import { ScrollView } from '@/components/ui/scroll-view';
const ItemList = ({ items }: { items: Item[] }) => (
{items.map((item) => (
{item.name}
))}
);
Why this is bad: All items are rendered at once, causing performance issues with long lists.
Platform-Specific Code
Use Platform.select for iOS/Android differences. This provides a clean, declarative way to handle platform-specific code.
Correct Pattern
// ✅ CORRECT: Using Platform.select
import { Platform } from 'react-native';
const styles = Platform.select({
ios: {
paddingTop: 20,
},
android: {
paddingTop: 0,
},
default: {
paddingTop: 0,
},
});
{/* Content */}
// ✅ CORRECT: Platform.select for values
const keyboardBehavior = Platform.select({
ios: 'padding',
android: 'height',
default: 'padding',
});
Incorrect Pattern
// ❌ INCORRECT: Using if/else for platform checks
import { Platform } from 'react-native';
let styles;
if (Platform.OS === 'ios') {
styles = { paddingTop: 20 };
} else {
styles = { paddingTop: 0 };
}
Best Practices Summary
| Practice | Why It Matters | When to Use | |----------|---------------|-------------| | TypeScript | Type safety, catch errors early | Always | | React.memo | Prevent unnecessary rerenders | Components with stable props | | useCallback | Stable function references | Callbacks passed to children | | Reanimated worklets | 60fps animations | All animations | | SafeAreaView | Handle device notches/bars | All screens | | FlatList | Virtualization for performance | Lists with 10+ items | | Platform.select | Clean platform-specific code | iOS/Android differences | | Real device testing | Accurate performance metrics | Before release |
Performance Optimization Patterns
Memoized List Item
// ✅ CORRECT: Memoized list item component
interface ItemProps {
readonly item: Item;
readonly onPress: (id: string) => void;
}
const ListItem = React.memo(({ item, onPress }: ItemProps) => {
const handlePress = useCallback(() => {
onPress(item.id);
}, [item.id, onPress]);
return (
{item.name}
);
});
// Parent component
const ItemList = ({ items }: { items: Item[] }) => {
const handlePress = useCallback((id: string) => {
// Handle press
}, []);
return (
(
)}
keyExtractor={(item) => item.id}
/>
);
};
Animated Component
// ✅ CORRECT: Using Reanimated for smooth animations
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
const AnimatedBox = Animated.createAnimatedComponent(Box);
const AnimatedCard = () => {
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
const handlePress = () => {
scale.value = withSpring(1.1);
};
return (
Press to animate
);
};
Safe Area Handling
// ✅ CORRECT: Proper safe area handling
import { SafeAreaView } from '@/components/ui/safe-area-view';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const Screen = () => {
const insets = useSafeAreaInsets();
return (
{/* Header */}
Title
{/* Content */}
{/* Content */}
{/* Footer with safe bottom padding */}
Submit
);
};
Performance Checklist
- [ ] TypeScript types defined for all components and props
- [ ] Expensive components wrapped with
React.memo - [ ] Callbacks memoized with
useCallback - [ ] Animations use Reanimated worklets
- [ ] Safe areas handled with
SafeAreaVieworuseSafeAreaInsets - [ ] Long lists use
FlatListinstead ofScrollView+map - [ ] Platform-specific code uses
Platform.select - [ ] Tested on real devices (not just simulators)
- [ ] Performance profiled and optimized
- [ ] Cross-platform compatibility verified (native + web)
Common Performance Pitfalls
❌ Don't: Use ScrollView for Long Lists
// ❌ INCORRECT: No virtualization
{items.map(item => )}
// ✅ CORRECT: Use FlatList
}
keyExtractor={(item) => item.id}
/>
❌ Don't: Create Callbacks Without useCallback
// ❌ INCORRECT: New function on every render
handlePress(id)}>
Press
// ✅ CORRECT: Memoized callback
const memoizedPress = useCallback(() => {
handlePress(id);
}, [id, handlePress]);
Press
❌ Don't: Use Animated API for Complex Animations
// ❌ INCORRECT: Runs on JavaScript thread
import { Animated } from 'react-native';
const animValue = new Animated.Value(0);
// ✅ CORRECT: Use Reanimated (runs on UI thread)
import { useSharedValue } from 'react-native-reanimated';
const animValue = useSharedValue(0);
v5-Specific Performance Notes
NativeWind v5
- Pin
lightningcssto exactly1.30.1inpackage.jsonoverridesandresolutions. Mismatched versions cause CSS transformation errors and build failures. - Tailwind v4 uses CSS-first configuration via
global.css— there is notailwind.config.jsoverhead. @tailwindcss/postcsshandles CSS processing; keep thepostcss.config.jsfile minimal.
UniWind
- No PostCSS / lightningcss — UniWind processes CSS at Metro/Babel time, eliminating the PostCSS build step entirely. This can reduce build times for Expo projects.
- Uses
:where()selectors for theme scoping, which have zero CSS specificity — no specificity wars between themes. - Consider UniWind for simpler Expo-only projects where build-step reduction matters.
Reference
- Reanimated Documentation: https://docs.swmansion.com/react-native-reanimated/
- React Native Performance: https://reactnative.dev/docs/performance
- Expo Router: https://docs.expo.dev/router/introduction/
- NativeWind v5: https://www.nativewind.dev/
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.