A clean React Native starter application with a production-ready architecture: typed API client with caching, authentication context, React Navigation, and data-fetching hooks.
src/
api/
cache.ts # In-memory cache with TTL (5 min default)
client.ts # Generic API client factory (GET/POST/PUT/PATCH/DELETE)
auth.ts # Auth API factory (login, register, logout, getMe, etc.)
index.ts # Configured API instance with AsyncStorage token persistence
hooks/
useApi.ts # useApiGet, useApiPost, useApiMutation hooks
contexts/
AuthContext.tsx # Auth state, login/register/logout, session validation
navigation/
types.ts # Typed param lists for every navigator
RootNavigator.tsx
AuthStack.tsx
MainTabs.tsx
screens/
LoginScreen.tsx
RegisterScreen.tsx
HomeScreen.tsx
ProfileScreen.tsx
types/
index.ts # Shared TypeScript types (User, AuthResponse, etc.)
- API calls go through the client, never called directly in components. Use hooks (
useApiGet,useApiMutation) or a service layer. - Hooks use a mounted ref to prevent state updates after unmount.
- Auth state is context-based — no Redux. Token is persisted in AsyncStorage and the session is validated against the server on app launch.
- Cache auto-invalidates on PUT/PATCH/DELETE for the affected endpoint and its parent.
- Navigation is fully typed via param list types.
| Tool | Minimum Version | Install |
|---|---|---|
| Node.js | 22.x | nvm or nodejs.org |
| Ruby | 3.0+ | brew install ruby or rbenv |
| CocoaPods | latest | gem install cocoapods (after Ruby 3.0+) |
| Xcode | 16+ | Mac App Store |
| Xcode CLI tools | latest | xcode-select --install |
git clone <your-repo-url> StarterApp
cd StarterApp
npm installEdit src/api/index.ts and set your API base URL:
const API_BASE_URL = 'https://your-api.example.com/api';cd ios
bundle install # installs the correct CocoaPods version via Bundler
bundle exec pod install
cd ..If you don't use Bundler, install CocoaPods globally:
gem install cocoapods
cd ios && pod install && cd ..open ios/StarterApp.xcworkspaceImportant: Always open the
.xcworkspacefile, not.xcodeproj. The workspace includes CocoaPods dependencies.
From Xcode:
- Select a simulator or connected device in the toolbar
- Press Cmd + R to build and run
From the terminal:
# Start Metro bundler
npm start
# In a second terminal, build and launch on iOS simulator
npm run ios- Add screen params to
src/navigation/types.ts - Create the screen component in
src/screens/ - Add it to the appropriate stack/tab navigator
Fetch data in a component:
import { useApiGet } from '../hooks';
import { api } from '../api';
function MyScreen() {
const { data, loading, error, refetch } = useApiGet<MyType>(api, '/my-endpoint');
// ...
}Trigger a mutation:
import { useApiMutation } from '../hooks';
import { api } from '../api';
function MyForm() {
const { mutate, loading } = useApiMutation(api, 'post');
const handleSubmit = async () => {
await mutate('/items', { name: 'New Item' });
};
}Or create a service for grouped operations:
// src/services/itemService.ts
import { api } from '../api';
export const itemService = {
getAll: () => api.get<Item[]>('/items'),
getById: (id: number) => api.get<Item>(`/items/${id}`),
create: (data: CreateItemData) => api.post<Item>('/items', data),
update: (id: number, data: Partial<Item>) => api.patch<Item>(`/items/${id}`, data),
remove: (id: number) => api.delete(`/items/${id}`),
};- Ensure Ruby 3.0+ is active:
ruby --version - Clear pod cache:
cd ios && pod cache clean --all && pod install
- Clean build: Product > Clean Build Folder (Cmd + Shift + K)
- Reset Metro cache:
npm start -- --reset-cache
- Open Xcode Settings > Platforms and install an iOS simulator runtime