npx claudepluginhub bee-coded/bee-dev --plugin beeThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
These standards apply when the project stack is react-native-expo. All agents and implementations must follow these conventions. This is a MOBILE-FIRST stack.
Also read skills/standards/frontend/SKILL.md for universal frontend standards (component architecture, accessibility, design quality) that apply alongside these React Native-specific conventions. Note: responsive design and CSS methodology sections are web-focused — for mobile, follow the patterns in this skill instead.
app.json or app.config.js for static/dynamic configuration (app name, slug, version, splash, icons).app.config.js when configuration needs environment variables or dynamic values.eas build --platform ios / eas build --platform android.eas submit --platform ios / eas submit --platform android.npx expo prebuild) for custom native code that Expo Go cannot support.npx expo start.expo SDK version consistent across all expo-* packages. Upgrade together.{
"expo": {
"name": "MyApp",
"slug": "my-app",
"version": "1.0.0",
"orientation": "portrait",
"sdkVersion": "52.0.0",
"plugins": ["expo-camera", "expo-location"]
}
}
View replaces div. Text replaces span, p, h1, etc. Image replaces img.ScrollView for short scrollable content. FlatList for long lists (virtualized, performant).Pressable for all touchable elements. TouchableOpacity is deprecated -- do not use it.<Text> components. Raw strings outside <Text> cause crashes.SectionList for grouped data. FlatList for flat data. Never use ScrollView + .map() for lists.KeyboardAvoidingView to handle keyboard overlap on forms.// Pattern: list with FlatList, not ScrollView + map
const OrderList = ({ orders }: { orders: Order[] }) => (
<FlatList
data={orders}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Pressable onPress={() => handlePress(item.id)}>
<View style={styles.row}>
<Text style={styles.title}>{item.name}</Text>
<Text style={styles.subtitle}>{item.status}</Text>
</View>
</Pressable>
)}
ListEmptyComponent={<Text>No orders found</Text>}
/>
);
app/ directory.app/index.tsx is /, app/orders/[id].tsx is /orders/:id._layout.tsx files define navigation structure (Stack, Tabs, Drawer).app/(tabs)/ with _layout.tsx defining tabs).<Link href="/orders"> for declarative navigation, router.push('/orders') for programmatic.redirect in _layout.tsx -- check auth state and redirect to login.// Pattern: tab layout with protected route
export default function TabLayout() {
const { user } = useAuth();
if (!user) return <Redirect href="/login" />;
return (
<Tabs>
<Tabs.Screen name="index" options={{ title: 'Home' }} />
<Tabs.Screen name="orders" options={{ title: 'Orders' }} />
<Tabs.Screen name="profile" options={{ title: 'Profile' }} />
</Tabs>
);
}
Platform.OS returns 'ios' | 'android' | 'web' for runtime checks.Platform.select({ ios: value, android: value, default: value }) for inline value selection.Component.ios.tsx, Component.android.tsx. The bundler resolves the correct file automatically.// Pattern: platform-specific styling
const styles = StyleSheet.create({
card: {
...Platform.select({
ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1 },
android: { elevation: 4 },
}),
backgroundColor: '#fff',
borderRadius: 8,
padding: 16,
},
});
StyleSheet.create() for ALL styles. Never pass inline style objects -- they cause unnecessary re-renders.flexDirection is column (not row like web CSS).useWindowDimensions() or Dimensions.get('window') for responsive sizing.useSafeAreaInsets() from react-native-safe-area-context for notch/home indicator handling.px, em, rem, % (except in flex ratios).gap property works in React Native for spacing between flex children.// Pattern: responsive layout with safe area
const { width } = useWindowDimensions();
const insets = useSafeAreaInsets();
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: insets.top,
paddingBottom: insets.bottom,
},
grid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
padding: 16,
},
card: {
width: width > 600 ? (width - 48 - 12) / 2 : width - 32,
},
});
npx expo install.expo-camera, expo-location, expo-notifications, expo-image-picker, expo-file-system, expo-secure-store, expo-haptics, expo-av.requestPermissionsAsync() from each module.npx expo install and add to app.json plugins if native config is needed.// Pattern: camera with permission handling
const [permission, requestPermission] = useCameraPermissions();
if (!permission?.granted) {
return (
<View style={styles.container}>
<Text>Camera access is required to scan barcodes</Text>
<Pressable onPress={requestPermission}>
<Text>Grant Permission</Text>
</Pressable>
</View>
);
}
return <CameraView style={styles.camera} facing="back" />;
useState, useReducer, useContext, useEffect, useMemo, useCallback.localStorage). Install via @react-native-async-storage/async-storage.expo-secure-store) for sensitive data: auth tokens, API keys, credentials.// Pattern: secure token storage
import * as SecureStore from 'expo-secure-store';
async function saveToken(token: string) {
await SecureStore.setItemAsync('auth_token', token);
}
async function getToken(): Promise<string | null> {
return SecureStore.getItemAsync('auth_token');
}
@testing-library/react-native) for component tests.render, screen, fireEvent are the primary utilities. Use fireEvent, NOT userEvent (RNTL uses fireEvent).jest.setup.js -- modules like expo-camera, expo-location are not available in Jest.waitFor() for async state updates. Use act() when triggering state changes outside events.jest.mock('expo-router') for testing navigation calls.// Pattern: component test with RNTL
import { render, screen, fireEvent } from '@testing-library/react-native';
describe('OrderCard', () => {
it('displays order name and status', () => {
render(<OrderCard name="Order #1" status="pending" />);
expect(screen.getByText('Order #1')).toBeTruthy();
expect(screen.getByText('pending')).toBeTruthy();
});
it('calls onPress with order id when pressed', () => {
const onPress = jest.fn();
render(<OrderCard id="123" name="Order #1" status="pending" onPress={onPress} />);
fireEvent.press(screen.getByText('Order #1'));
expect(onPress).toHaveBeenCalledWith('123');
});
});
div, span, p, h1, button) -- use View, Text, Pressable, etc.<Text> components -- this causes a runtime crash.TouchableOpacity -- it is deprecated. Use Pressable instead.ScrollView with .map() for long lists -- use FlatList for virtualized rendering.style={{ padding: 10 }}) -- use StyleSheet.create() to avoid re-renders.flexDirection is row -- React Native defaults to column (opposite of web CSS).localStorage -- it does not exist in React Native. Use AsyncStorage or SecureStore.userEvent from RNTL -- React Native Testing Library uses fireEvent, not userEvent..ts / .tsx extensions. Define typed props interfaces for every component. No untyped code.jest-expo preset and React Native Testing Library. Follow Red-Green-Refactor.Platform.select for platform-specific values. Use Platform.select({ ios: value, android: value, default: value }) instead of ternary expressions with Platform.OS.useLocalSearchParams<{ id: string }>() and typed router.push() calls for type-safe navigation.strict mode in tsconfig.json. Handle nullable values explicitly -- no implicit undefined access.requestPermissionsAsync() and handle the denied case before accessing camera, location, notifications, or media library.StyleSheet.create() for all styles. Define styles outside the component body. This avoids creating new style objects on every render and enables style validation.FlatList for all list rendering. Use FlatList with keyExtractor and renderItem for virtualized, performant scrolling. Reserve ScrollView for non-list scrollable content.useMemo for expensive computations. Memoize filtered lists, sorted data, and complex derived values to avoid recalculating on every render.expo-camera, expo-location, expo-image-picker, expo-file-system, expo-haptics, expo-notifications and other Expo SDK modules. They are tested against the managed workflow and upgrade cleanly with the SDK.useCallback for stable callback references. Wrap event handlers passed to child components or FlatList renderItem with useCallback to prevent unnecessary re-renders.KeyboardAvoidingView on form screens. The keyboard covers input fields on iOS. Wrap form screens in KeyboardAvoidingView with behavior="padding" on iOS and behavior="height" on Android.AsyncStorage.getItem() and setItem() return promises. Forgetting to await these calls leads to reading undefined instead of stored values and silently dropping writes.BackHandler.addEventListener('hardwareBackPress', handler) in useEffect with cleanup, or handle via Expo Router's back behavior.GestureHandlerRootView at app root. Gesture-based components (swipeable rows, bottom sheets, drawer navigation) silently fail without GestureHandlerRootView wrapping the app root. Add it in the root _layout.tsx.shadowColor, shadowOffset, shadowOpacity, shadowRadius. Android ignores these and uses elevation instead. Always provide both via Platform.select or the shadow styles will be invisible on one platform.useEffect and event handlers. Referencing state variables inside callbacks without listing them in the dependency array causes handlers to capture outdated values. Always include dependencies or use useRef for mutable values.android:usesCleartextTraffic in app.json network security config.class extends React.Component. All components must be function components with hooks. Class components are incompatible with modern React patterns and Expo conventions.any type. Never use any as a type annotation. Define explicit interfaces, union types, or generics. any disables TypeScript checking and hides bugs at compile time.expo-secure-store for all sensitive data.InteractionManager.runAfterInteractions(), web workers, or move work to native modules.style={{ marginTop: 10 }} in JSX. Inline objects are re-created on every render, causing unnecessary re-renders of child components. Use StyleSheet.create().index as key in dynamic lists. Never use array index as the key prop for lists where items can be reordered, inserted, or deleted. Use a stable unique identifier from the data.OrderCard.tsx, ProfileScreen.tsx. Hooks use camelCase with use prefix: useAuth.ts.app/ directory for Expo Router file-based routing. All screens and layouts live in the app/ directory. File names map directly to URL paths. Use _layout.tsx for navigation structure.components/ directory for shared UI components. Reusable components live in a top-level components/ directory (or src/components/), organized by feature or domain.snake_case API responses mapped to camelCase in the app. Backend APIs typically return snake_case keys. Transform them to camelCase at the API boundary (in the fetch/axios layer) so all app code uses consistent camelCase naming.hooks/ directory for custom hooks. Extract reusable logic into custom hooks stored in hooks/ (or src/hooks/). Each hook file exports a single use* function.constants/ directory for app-wide constants. Colors, spacing values, API endpoints, and configuration constants live in a constants/ directory, not scattered across components.StyleSheet.create().When looking up framework documentation, use these Context7 library identifiers:
facebook/react-native -- core components, APIs, styling, platform modulesexpo/expo -- SDK modules, configuration, EAS Build, Expo Routerexpo/router -- file-based routing, layouts, navigation, deep linkingcallstack/react-native-testing-library -- render, screen, fireEvent, queriesAlways check Context7 for the latest API when working with Expo SDK version-specific features. Training data may be outdated for recent Expo SDK releases.