---
name: react-native-expo
description: Add new screens or features to a React Native Expo app. Covers screen creation, navigation, API integration, and UI patterns. Use when the user asks to add a screen, mobile page, React Native component, Expo screen, or mobile feature.
license: CC-BY-ND-4.0
---
# React Native Expo Screen
## New screen checklist
1. Create screen file in `app/(app)/` (or appropriate route group)
2. Add to navigation (tab bar or stack)
3. Create any API hooks needed in `src/api/`
4. Add types to `src/types/` if new data shapes
## Screen template
```typescript
import { View, Text, StyleSheet, ScrollView } from 'react-native';
import { useRouter } from 'expo-router';
import { useQuery } from '@tanstack/react-query';
import { api } from '@/src/api/client';
export default function MyScreen() {
const router = useRouter();
const { data, isLoading, error } = useQuery({
queryKey: ['myData'],
queryFn: () => api.get('/my-endpoint').then(r => r.data),
});
if (isLoading) return ;
if (error) return ;
return (
Screen Title
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#fff' },
title: { fontSize: 24, fontWeight: 'bold', padding: 16 },
});
```
## OTP Auth screens
### Login screen (`app/(auth)/login.tsx`)
```typescript
// 1. Input: email or phone number
// 2. Call POST /auth/otp/send
// 3. Navigate to verify screen with email param
router.push({ pathname: '/(auth)/verify', params: { email } });
```
### Verify screen (`app/(auth)/verify.tsx`)
```typescript
// 1. Input: 6-digit OTP code
// 2. Call POST /auth/otp/verify
// 3. Store JWT: await SecureStore.setItemAsync('auth_token', token)
// 4. Navigate to app: router.replace('/(app)')
```
## Navigation patterns
### Tab bar (in `app/(app)/_layout.tsx`)
```typescript
import { Tabs } from 'expo-router';
import { Home, FileText, User } from 'lucide-react-native';
export default function AppLayout() {
return (
}} />
}} />
);
}
```
### Auth guard (root `app/_layout.tsx`)
```typescript
const token = await SecureStore.getItemAsync('auth_token');
if (!token) {
router.replace('/(auth)/login');
}
```
## API client setup
```typescript
// src/api/client.ts
import axios from 'axios';
import * as SecureStore from 'expo-secure-store';
export const api = axios.create({ baseURL: process.env.EXPO_PUBLIC_API_URL });
api.interceptors.request.use(async config => {
const token = await SecureStore.getItemAsync('auth_token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
```
## Environment variables
Use `EXPO_PUBLIC_` prefix for variables exposed to the app:
```
EXPO_PUBLIC_API_URL=https://your-api.execute-api.eu-west-1.amazonaws.com
```
## Profile screen essentials
- Display user name, email
- Organization switcher (if multi-tenant)
- Logout button: clear SecureStore + router.replace('/(auth)/login')
## Splash screen setup
```json
// app.json
{
"expo": {
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
}
}
}
```
```typescript
// app/_layout.tsx
import * as SplashScreen from 'expo-splash-screen';
SplashScreen.preventAutoHideAsync();
// After fonts/auth check:
SplashScreen.hideAsync();
```