Production-ready React Native template with Expo, authentication, i18n, offline support, and more
npm install @croacroa/react-native-templatebash
npx create-expo-app my-app --template @croacroa/react-native-template
cd my-app
npm install
`
$3
`bash
npx degit croacroa-dev-team/template-react-native my-app
cd my-app
./scripts/init.sh # macOS/Linux
or
.\scripts\init.ps1 # Windows PowerShell
`
$3
`bash
git clone https://github.com/croacroa-dev-team/template-react-native my-app
cd my-app
rm -rf .git
npm install
cp .env.example .env
`
Then update:
- app.config.ts - App name, bundle ID, scheme
- package.json - Package name
- constants/config.ts - API URLs
$3
`bash
npm start # Start development server
npm run ios # Run on iOS simulator
npm run android # Run on Android emulator
`
๐ Project Structure
`
โโโ app/ # Expo Router pages
โ โโโ (auth)/ # Protected routes (home, profile, settings)
โ โโโ (public)/ # Public routes (login, register, forgot-password)
โ โโโ _layout.tsx # Root layout with providers
โโโ components/
โ โโโ ui/ # UI components (Button, Card, Modal, Skeleton)
โ โโโ forms/ # Form components (FormInput)
โ โโโ ErrorBoundary.tsx # Global error handling
โโโ hooks/ # useAuth, useTheme, useNotifications, useApi, useOffline
โโโ stores/ # Zustand stores (appStore, notificationStore)
โโโ services/
โ โโโ api.ts # HTTP client with 401 retry
โ โโโ queryClient.ts # TanStack Query with persistence
โ โโโ sentry.ts # Crash reporting
โ โโโ storage.ts # AsyncStorage & SecureStore helpers
โโโ utils/ # cn, toast, validation schemas
โโโ constants/ # App configuration
โโโ types/ # TypeScript types
โโโ __tests__/ # Test files (58+ tests)
โโโ scripts/ # Init scripts for template setup
`
๐ Authentication
Complete auth flow with automatic token refresh:
`tsx
import { useAuth } from "@/hooks/useAuth";
function MyComponent() {
const {
user,
isAuthenticated,
isLoading,
signIn,
signUp,
signOut,
updateUser,
refreshSession,
} = useAuth();
}
`
Features:
- Tokens stored securely with expo-secure-store
- Automatic refresh 5 minutes before expiry
- Race condition handling for concurrent requests
- Redirect to login on session expiry
๐ก API Client
Robust HTTP client with automatic retry:
`tsx
import { api } from "@/services/api";
// Basic requests
const users = await api.get("/users");
const user = await api.post("/users", { name: "John" });
await api.put("/users/1", { name: "Jane" });
await api.delete("/users/1");
// Skip auth for public endpoints
await api.get("/public", { requiresAuth: false });
`
$3
The API client automatically:
1. Catches 401 responses
2. Refreshes the access token
3. Retries the original request
4. Redirects to login if refresh fails
๐ Data Fetching
TanStack Query with offline persistence:
`tsx
import { useCurrentUser, useUpdateUser } from "@/hooks/useApi";
function Profile() {
const { data: user, isLoading, error } = useCurrentUser();
const updateUser = useUpdateUser();
const handleUpdate = () => {
updateUser.mutate(
{ name: "New Name" },
{ onSuccess: () => toast.success("Updated!") }
);
};
}
`
$3
Create hooks for any resource:
`tsx
import { createCrudHooks } from "@/hooks/useApi";
const postsApi = createCrudHooks({
baseKey: ["posts"],
endpoint: "/posts",
entityName: "Post",
});
// Usage
const { data: posts } = postsApi.useList();
const { data: post } = postsApi.useById("123");
const createPost = postsApi.useCreate();
`
๐ด Offline Support
Automatic offline handling:
`tsx
import { useOffline } from "@/hooks/useOffline";
function MyComponent() {
const { isOffline, isOnline } = useOffline({ showToast: true });
// Shows toast when connection lost/restored
}
`
Query cache persisted to AsyncStorage - data available offline.
๐จ Skeleton Loaders
Pre-built skeleton components:
`tsx
import {
Skeleton,
SkeletonText,
SkeletonCard,
SkeletonProfile,
SkeletonList,
} from "@/components/ui/Skeleton";
// Single skeleton
// Profile placeholder
// List of cards
`
๐ Toast Notifications
Centralized toast system:
`tsx
import { toast, handleApiError } from "@/utils/toast";
// Simple toasts
toast.success("Profile updated");
toast.error("Something went wrong", "Please try again");
toast.info("New message received");
// Handle API errors automatically
try {
await api.post("/endpoint", data);
} catch (error) {
handleApiError(error); // Shows appropriate toast
}
`
๐ก๏ธ Error Boundary
Global error handling with Sentry:
`tsx
// Already wrapped in _layout.tsx
;
// Or use HOC for specific components
import { withErrorBoundary } from "@/components/ErrorBoundary";
const SafeComponent = withErrorBoundary(RiskyComponent);
`
๐ Form Validation
React Hook Form + Zod:
`tsx
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { FormInput } from "@/components/forms";
import { loginSchema, LoginFormData } from "@/utils/validation";
function LoginForm() {
const { control, handleSubmit } = useForm({
resolver: zodResolver(loginSchema),
});
return (
name="email"
control={control}
label="Email"
keyboardType="email-address"
/>
);
}
`
Pre-built schemas: loginSchema, registerSchema, forgotPasswordSchema, profileSchema
๐ญ Theming
Dark/light mode with persistence:
`tsx
import { useTheme } from "@/hooks/useTheme";
function MyComponent() {
const { isDark, mode, toggleTheme, setMode } = useTheme();
// mode: 'light' | 'dark' | 'system'
}
`
๐ง Configuration
$3
`env
.env
EXPO_PUBLIC_SENTRY_DSN=your-sentry-dsn
`
$3
1. Create project at sentry.io
2. Copy DSN to .env
3. Errors automatically reported in production
๐งช Testing
58+ tests included:
`bash
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # With coverage
`
Test coverage:
- useAuth hook - 24 tests
- ApiClient - 22 tests
- UI components - 12 tests
๐ Available Scripts
| Command | Description |
| ----------------------- | ------------------------ |
| npm start | Start Expo dev server |
| npm run ios | Run on iOS simulator |
| npm run android | Run on Android emulator |
| npm test | Run tests |
| npm run storybook | Start Storybook |
| npm run lint | Run ESLint |
| npm run typecheck | TypeScript check |
| npm run build:dev | Build development client |
| npm run build:preview | Build preview APK/IPA |
| npm run build:prod | Build production release |
โ
Customization Checklist
- [ ] Run init script or manually update placeholders
- [ ] Replace icons in assets/images/
- [ ] Configure API URL in constants/config.ts
- [ ] Set up Sentry DSN in .env
- [ ] Configure EAS: eas build:configure
- [ ] Implement real API calls in services/api.ts`