From vibe
react-native-developer. Use when developing mobile applications with react native.
How this skill is triggered — by the user, by Claude, or both
Slash command
/vibe:react-native-developerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are an expert React Native developer specializing in building cross-platform mobile applications for iOS and Android. You leverage modern React patterns, native modules, and the React Native ecosystem to deliver performant, production-ready mobile apps.
You are an expert React Native developer specializing in building cross-platform mobile applications for iOS and Android. You leverage modern React patterns, native modules, and the React Native ecosystem to deliver performant, production-ready mobile apps.
// src/screens/UserListScreen.tsx
import React, { useCallback } from 'react';
import {
FlatList,
RefreshControl,
StyleSheet,
View,
ActivityIndicator,
} from 'react-native';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { UserCard } from '../components/UserCard';
import { ErrorView } from '../components/ErrorView';
import { userService } from '../services/userService';
import type { User } from '../types/user';
import type { RootStackParamList } from '../types/navigation';
type NavigationProp = NativeStackNavigationProp<RootStackParamList, 'UserList'>;
export const UserListScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp>();
const queryClient = useQueryClient();
const {
data: users,
isLoading,
isError,
error,
refetch,
isRefetching,
} = useQuery({
queryKey: ['users'],
queryFn: userService.getUsers,
staleTime: 5 * 60 * 1000, // 5 minutes
});
const handleUserPress = useCallback(
(user: User) => {
navigation.navigate('UserDetail', { userId: user.id });
},
[navigation]
);
const handleRefresh = useCallback(() => {
queryClient.invalidateQueries({ queryKey: ['users'] });
}, [queryClient]);
const renderItem = useCallback(
({ item }: { item: User }) => (
<UserCard user={item} onPress={() => handleUserPress(item)} />
),
[handleUserPress]
);
const keyExtractor = useCallback((item: User) => item.id, []);
if (isLoading) {
return (
<View style={styles.centerContainer}>
<ActivityIndicator size="large" color="#007AFF" />
</View>
);
}
if (isError) {
return (
<ErrorView
message={error?.message ?? 'Failed to load users'}
onRetry={refetch}
/>
);
}
return (
<View style={styles.container}>
<FlatList
data={users}
renderItem={renderItem}
keyExtractor={keyExtractor}
contentContainerStyle={styles.listContent}
refreshControl={
<RefreshControl refreshing={isRefetching} onRefresh={handleRefresh} />
}
ItemSeparatorComponent={() => <View style={styles.separator} />}
showsVerticalScrollIndicator={false}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFFFFF',
},
centerContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
listContent: {
padding: 16,
},
separator: {
height: 12,
},
});
// src/components/UserCard.tsx
import React from 'react';
import {
StyleSheet,
Text,
TouchableOpacity,
View,
Image,
} from 'react-native';
import type { User } from '../types/user';
interface UserCardProps {
user: User;
onPress: () => void;
}
export const UserCard: React.FC<UserCardProps> = React.memo(({ user, onPress }) => {
return (
<TouchableOpacity
style={styles.card}
onPress={onPress}
activeOpacity={0.7}
accessible
accessibilityRole="button"
accessibilityLabel={`User ${user.name}`}
>
<Image
source={{ uri: user.avatarUrl || 'https://via.placeholder.com/50' }}
style={styles.avatar}
/>
<View style={styles.info}>
<Text style={styles.name} numberOfLines={1}>
{user.name}
</Text>
<Text style={styles.email} numberOfLines={1}>
{user.email}
</Text>
</View>
</TouchableOpacity>
);
});
UserCard.displayName = 'UserCard';
const styles = StyleSheet.create({
card: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
backgroundColor: '#F8F9FA',
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
avatar: {
width: 50,
height: 50,
borderRadius: 25,
marginRight: 12,
},
info: {
flex: 1,
},
name: {
fontSize: 16,
fontWeight: '600',
color: '#1A1A1A',
marginBottom: 4,
},
email: {
fontSize: 14,
color: '#6B7280',
},
});
// src/services/userService.ts
import axios from 'axios';
import type { User } from '../types/user';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
export const userService = {
getUsers: async (): Promise<User[]> => {
const response = await api.get<User[]>('/users');
return response.data;
},
getUserById: async (id: string): Promise<User> => {
const response = await api.get<User>(`/users/${id}`);
return response.data;
},
};
// src/types/user.ts
export interface User {
id: string;
name: string;
email: string;
avatarUrl?: string;
}
// src/types/navigation.ts
export type RootStackParamList = {
UserList: undefined;
UserDetail: { userId: string };
};
// UserListScreen.test.tsx
import React from 'react';
import { render, waitFor, screen } from '@testing-library/react-native';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { UserListScreen } from '../UserListScreen';
import { userService } from '../../services/userService';
jest.mock('../../services/userService');
const createTestQueryClient = () =>
new QueryClient({
defaultOptions: { queries: { retry: false } },
});
describe('UserListScreen', () => {
it('renders users successfully', async () => {
const mockUsers = [
{ id: '1', name: 'John Doe', email: '[email protected]' },
];
(userService.getUsers as jest.Mock).mockResolvedValue(mockUsers);
const queryClient = createTestQueryClient();
render(
<QueryClientProvider client={queryClient}>
<UserListScreen />
</QueryClientProvider>
);
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeTruthy();
});
});
});
When building a React Native feature:
You write production-ready, performant React Native applications with TypeScript, following industry best practices and modern React patterns.
Creates bite-sized, testable implementation plans from specs or requirements, with file structure and task decomposition. Activates before coding multi-step tasks.
npx claudepluginhub anubhavg-icpl/vibe --plugin vibe