@croacroa/react-native-template 2.1.0 โ†’ 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.env.example +5 -0
  2. package/.eslintrc.js +8 -0
  3. package/.github/workflows/ci.yml +187 -187
  4. package/.github/workflows/eas-build.yml +55 -55
  5. package/.github/workflows/eas-update.yml +50 -50
  6. package/.github/workflows/npm-publish.yml +57 -0
  7. package/CHANGELOG.md +195 -106
  8. package/CONTRIBUTING.md +377 -377
  9. package/LICENSE +21 -21
  10. package/README.md +446 -402
  11. package/__tests__/accessibility/components.test.tsx +285 -0
  12. package/__tests__/components/Button.test.tsx +2 -4
  13. package/__tests__/components/__snapshots__/snapshots.test.tsx.snap +512 -0
  14. package/__tests__/components/snapshots.test.tsx +131 -131
  15. package/__tests__/helpers/a11y.ts +54 -0
  16. package/__tests__/hooks/useAnalytics.test.ts +100 -0
  17. package/__tests__/hooks/useAnimations.test.ts +70 -0
  18. package/__tests__/hooks/useAuth.test.tsx +71 -28
  19. package/__tests__/hooks/useMedia.test.ts +318 -0
  20. package/__tests__/hooks/usePayments.test.tsx +307 -0
  21. package/__tests__/hooks/usePermission.test.ts +230 -0
  22. package/__tests__/hooks/useWebSocket.test.ts +329 -0
  23. package/__tests__/integration/auth-api.test.tsx +224 -227
  24. package/__tests__/performance/VirtualizedList.perf.test.tsx +385 -362
  25. package/__tests__/services/api.test.ts +24 -6
  26. package/app/(auth)/home.tsx +11 -9
  27. package/app/(auth)/profile.tsx +8 -6
  28. package/app/(auth)/settings.tsx +11 -9
  29. package/app/(public)/forgot-password.tsx +25 -15
  30. package/app/(public)/login.tsx +48 -12
  31. package/app/(public)/onboarding.tsx +5 -5
  32. package/app/(public)/register.tsx +24 -15
  33. package/app/_layout.tsx +6 -3
  34. package/app.config.ts +27 -2
  35. package/assets/images/.gitkeep +7 -7
  36. package/assets/images/adaptive-icon.png +0 -0
  37. package/assets/images/favicon.png +0 -0
  38. package/assets/images/icon.png +0 -0
  39. package/assets/images/notification-icon.png +0 -0
  40. package/assets/images/splash.png +0 -0
  41. package/components/ErrorBoundary.tsx +73 -28
  42. package/components/auth/SocialLoginButtons.tsx +168 -0
  43. package/components/forms/FormInput.tsx +5 -3
  44. package/components/onboarding/OnboardingScreen.tsx +370 -370
  45. package/components/onboarding/index.ts +2 -2
  46. package/components/providers/AnalyticsProvider.tsx +67 -0
  47. package/components/providers/SuspenseBoundary.tsx +359 -357
  48. package/components/providers/index.ts +24 -21
  49. package/components/ui/AnimatedButton.tsx +1 -9
  50. package/components/ui/AnimatedList.tsx +98 -0
  51. package/components/ui/AnimatedScreen.tsx +89 -0
  52. package/components/ui/Avatar.tsx +319 -316
  53. package/components/ui/Badge.tsx +416 -416
  54. package/components/ui/BottomSheet.tsx +307 -307
  55. package/components/ui/Button.tsx +11 -3
  56. package/components/ui/Checkbox.tsx +261 -261
  57. package/components/ui/FeatureGate.tsx +57 -0
  58. package/components/ui/ForceUpdateScreen.tsx +108 -0
  59. package/components/ui/ImagePickerButton.tsx +180 -0
  60. package/components/ui/Input.stories.tsx +2 -10
  61. package/components/ui/Input.tsx +2 -10
  62. package/components/ui/OptimizedImage.tsx +369 -369
  63. package/components/ui/Paywall.tsx +253 -0
  64. package/components/ui/PermissionGate.tsx +155 -0
  65. package/components/ui/PurchaseButton.tsx +84 -0
  66. package/components/ui/Select.tsx +240 -240
  67. package/components/ui/Skeleton.tsx +3 -1
  68. package/components/ui/Toast.tsx +427 -418
  69. package/components/ui/UploadProgress.tsx +189 -0
  70. package/components/ui/VirtualizedList.tsx +288 -285
  71. package/components/ui/index.ts +28 -30
  72. package/constants/config.ts +135 -97
  73. package/docs/adr/001-state-management.md +79 -79
  74. package/docs/adr/002-styling-approach.md +130 -130
  75. package/docs/adr/003-data-fetching.md +155 -155
  76. package/docs/adr/004-auth-adapter-pattern.md +144 -144
  77. package/docs/adr/README.md +78 -78
  78. package/docs/guides/analytics-posthog.md +121 -0
  79. package/docs/guides/auth-supabase.md +162 -0
  80. package/docs/guides/feature-flags-launchdarkly.md +150 -0
  81. package/docs/guides/payments-revenuecat.md +169 -0
  82. package/docs/plans/2026-02-22-phase6-implementation.md +3222 -0
  83. package/docs/plans/2026-02-22-phase6-template-completion-design.md +196 -0
  84. package/docs/plans/2026-02-23-npm-publish-design.md +31 -0
  85. package/docs/plans/2026-02-23-phase7-polish-documentation-design.md +79 -0
  86. package/docs/plans/2026-02-23-phase8-additional-features-design.md +136 -0
  87. package/eas.json +2 -1
  88. package/hooks/index.ts +70 -40
  89. package/hooks/useAnimatedEntry.ts +204 -0
  90. package/hooks/useApi.ts +5 -4
  91. package/hooks/useAuth.tsx +7 -3
  92. package/hooks/useBiometrics.ts +295 -295
  93. package/hooks/useChannel.ts +111 -0
  94. package/hooks/useDeepLinking.ts +256 -256
  95. package/hooks/useExperiment.ts +36 -0
  96. package/hooks/useFeatureFlag.ts +59 -0
  97. package/hooks/useForceUpdate.ts +91 -0
  98. package/hooks/useImagePicker.ts +281 -375
  99. package/hooks/useInAppReview.ts +64 -0
  100. package/hooks/useMFA.ts +509 -499
  101. package/hooks/useParallax.ts +142 -0
  102. package/hooks/usePerformance.ts +434 -434
  103. package/hooks/usePermission.ts +190 -0
  104. package/hooks/usePresence.ts +129 -0
  105. package/hooks/useProducts.ts +36 -0
  106. package/hooks/usePurchase.ts +103 -0
  107. package/hooks/useRateLimit.ts +70 -0
  108. package/hooks/useSubscription.ts +49 -0
  109. package/hooks/useTrackEvent.ts +52 -0
  110. package/hooks/useTrackScreen.ts +40 -0
  111. package/hooks/useUpdates.ts +358 -358
  112. package/hooks/useUpload.ts +165 -0
  113. package/hooks/useWebSocket.ts +111 -0
  114. package/i18n/index.ts +197 -194
  115. package/i18n/locales/ar.json +170 -101
  116. package/i18n/locales/de.json +170 -101
  117. package/i18n/locales/en.json +170 -101
  118. package/i18n/locales/es.json +170 -101
  119. package/i18n/locales/fr.json +170 -101
  120. package/jest.config.js +1 -1
  121. package/maestro/README.md +113 -113
  122. package/maestro/config.yaml +35 -35
  123. package/maestro/flows/login.yaml +62 -62
  124. package/maestro/flows/mfa-login.yaml +92 -92
  125. package/maestro/flows/mfa-setup.yaml +86 -86
  126. package/maestro/flows/navigation.yaml +68 -68
  127. package/maestro/flows/offline-conflict.yaml +101 -101
  128. package/maestro/flows/offline-sync.yaml +128 -128
  129. package/maestro/flows/offline.yaml +60 -60
  130. package/maestro/flows/register.yaml +94 -94
  131. package/package.json +188 -176
  132. package/scripts/generate-placeholders.js +38 -0
  133. package/services/analytics/adapters/console.ts +50 -0
  134. package/services/analytics/analytics-adapter.ts +94 -0
  135. package/services/analytics/types.ts +73 -0
  136. package/services/analytics.ts +428 -428
  137. package/services/api.ts +419 -340
  138. package/services/auth/social/apple.ts +110 -0
  139. package/services/auth/social/google.ts +159 -0
  140. package/services/auth/social/social-auth.ts +100 -0
  141. package/services/auth/social/types.ts +80 -0
  142. package/services/authAdapter.ts +333 -333
  143. package/services/backgroundSync.ts +652 -626
  144. package/services/feature-flags/adapters/mock.ts +108 -0
  145. package/services/feature-flags/feature-flag-adapter.ts +174 -0
  146. package/services/feature-flags/types.ts +79 -0
  147. package/services/force-update.ts +140 -0
  148. package/services/index.ts +116 -54
  149. package/services/media/compression.ts +91 -0
  150. package/services/media/media-picker.ts +151 -0
  151. package/services/media/media-upload.ts +160 -0
  152. package/services/payments/adapters/mock.ts +159 -0
  153. package/services/payments/payment-adapter.ts +118 -0
  154. package/services/payments/types.ts +131 -0
  155. package/services/permissions/permission-manager.ts +284 -0
  156. package/services/permissions/types.ts +104 -0
  157. package/services/realtime/types.ts +100 -0
  158. package/services/realtime/websocket-manager.ts +441 -0
  159. package/services/security.ts +289 -286
  160. package/services/sentry.ts +4 -4
  161. package/stores/appStore.ts +9 -0
  162. package/stores/notificationStore.ts +3 -1
  163. package/tailwind.config.js +47 -47
  164. package/tsconfig.json +37 -13
  165. package/types/user.ts +1 -1
  166. package/utils/accessibility.ts +446 -446
  167. package/utils/animations/presets.ts +182 -0
  168. package/utils/animations/transitions.ts +62 -0
  169. package/utils/index.ts +63 -52
  170. package/utils/toast.ts +9 -2
  171. package/utils/validation.ts +4 -1
  172. package/utils/withAccessibility.tsx +272 -272
package/README.md CHANGED
@@ -1,402 +1,446 @@
1
- # @croacroa/react-native-template
2
-
3
- [![npm version](https://img.shields.io/npm/v/@croacroa/react-native-template.svg)](https://www.npmjs.com/package/@croacroa/react-native-template)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
6
- [![Expo SDK](https://img.shields.io/badge/Expo-SDK%2052-000020.svg)](https://expo.dev/)
7
- [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/croacroa-dev-team/template-react-native/pulls)
8
-
9
- A production-ready React Native template with Expo SDK 52, featuring authentication, i18n, biometrics, offline support, and more.
10
-
11
- ## โœจ Features
12
-
13
- ### Core
14
-
15
- - **Expo SDK 52** with TypeScript
16
- - **Expo Router** for file-based navigation
17
- - **NativeWind** (Tailwind CSS) for styling
18
- - **Zustand** for state management
19
- - **TanStack Query** with offline persistence
20
- - **React Hook Form + Zod** for form validation
21
-
22
- ### Authentication & Security
23
-
24
- - **Auth Adapter Pattern** - Easy switching between Supabase, Firebase, etc.
25
- - **Biometric Auth** - Face ID / Touch ID support
26
- - **Secure token storage** with expo-secure-store
27
- - **Automatic token refresh** with race condition handling
28
-
29
- ### Internationalization
30
-
31
- - **i18n** with expo-localization + i18next
32
- - **English & French** translations included
33
- - **Language detection** and persistence
34
-
35
- ### UX Features
36
-
37
- - **Dark/Light Theme** with system preference support
38
- - **Onboarding Screens** with animated pagination
39
- - **Push Notifications** with Expo Notifications
40
- - **Toast Notifications** with Burnt
41
- - **Deep Linking** support with route parsing
42
- - **Skeleton Loaders** with shimmer animation
43
- - **Offline Support** with connection status toasts
44
- - **OTA Updates** with expo-updates integration
45
-
46
- ### UI Components
47
-
48
- - Button, Input, Card, Modal, Skeleton
49
- - **Select/Dropdown**, Checkbox, Switch
50
- - **BottomSheet** with @gorhom/bottom-sheet
51
- - **Avatar** with initials fallback
52
- - **Badge, Chip, CountBadge**
53
- - **OptimizedImage** with expo-image
54
-
55
- ### DevOps & Quality
56
-
57
- - **GitHub Actions** CI/CD workflows
58
- - **Maestro** E2E tests
59
- - **Sentry** for crash reporting
60
- - **Analytics Adapter** for multiple providers
61
- - **Performance Monitoring** hooks
62
- - **Accessibility** utilities and hooks
63
- - **Jest + Testing Library** with 58+ tests
64
- - **Storybook** for component documentation
65
- - **ESLint + Prettier + Husky** for code quality
66
-
67
- ## ๐Ÿš€ Quick Start
68
-
69
- ### Option 1: Using npx (Recommended)
70
-
71
- ```bash
72
- npx create-expo-app my-app --template @croacroa/react-native-template
73
- cd my-app
74
- npm install
75
- ```
76
-
77
- ### Option 2: Using degit
78
-
79
- ```bash
80
- npx degit croacroa-dev-team/template-react-native my-app
81
- cd my-app
82
- ./scripts/init.sh # macOS/Linux
83
- # or
84
- .\scripts\init.ps1 # Windows PowerShell
85
- ```
86
-
87
- ### Option 3: Clone Repository
88
-
89
- ```bash
90
- git clone https://github.com/croacroa-dev-team/template-react-native my-app
91
- cd my-app
92
- rm -rf .git
93
- npm install
94
- cp .env.example .env
95
- ```
96
-
97
- Then update:
98
-
99
- - `app.config.ts` - App name, bundle ID, scheme
100
- - `package.json` - Package name
101
- - `constants/config.ts` - API URLs
102
-
103
- ### Run the App
104
-
105
- ```bash
106
- npm start # Start development server
107
- npm run ios # Run on iOS simulator
108
- npm run android # Run on Android emulator
109
- ```
110
-
111
- ## ๐Ÿ“ Project Structure
112
-
113
- ```
114
- โ”œโ”€โ”€ app/ # Expo Router pages
115
- โ”‚ โ”œโ”€โ”€ (auth)/ # Protected routes (home, profile, settings)
116
- โ”‚ โ”œโ”€โ”€ (public)/ # Public routes (login, register, forgot-password)
117
- โ”‚ โ””โ”€โ”€ _layout.tsx # Root layout with providers
118
- โ”œโ”€โ”€ components/
119
- โ”‚ โ”œโ”€โ”€ ui/ # UI components (Button, Card, Modal, Skeleton)
120
- โ”‚ โ”œโ”€โ”€ forms/ # Form components (FormInput)
121
- โ”‚ โ””โ”€โ”€ ErrorBoundary.tsx # Global error handling
122
- โ”œโ”€โ”€ hooks/ # useAuth, useTheme, useNotifications, useApi, useOffline
123
- โ”œโ”€โ”€ stores/ # Zustand stores (appStore, notificationStore)
124
- โ”œโ”€โ”€ services/
125
- โ”‚ โ”œโ”€โ”€ api.ts # HTTP client with 401 retry
126
- โ”‚ โ”œโ”€โ”€ queryClient.ts # TanStack Query with persistence
127
- โ”‚ โ”œโ”€โ”€ sentry.ts # Crash reporting
128
- โ”‚ โ””โ”€โ”€ storage.ts # AsyncStorage & SecureStore helpers
129
- โ”œโ”€โ”€ utils/ # cn, toast, validation schemas
130
- โ”œโ”€โ”€ constants/ # App configuration
131
- โ”œโ”€โ”€ types/ # TypeScript types
132
- โ”œโ”€โ”€ __tests__/ # Test files (58+ tests)
133
- โ””โ”€โ”€ scripts/ # Init scripts for template setup
134
- ```
135
-
136
- ## ๐Ÿ” Authentication
137
-
138
- Complete auth flow with automatic token refresh:
139
-
140
- ```tsx
141
- import { useAuth } from "@/hooks/useAuth";
142
-
143
- function MyComponent() {
144
- const {
145
- user,
146
- isAuthenticated,
147
- isLoading,
148
- signIn,
149
- signUp,
150
- signOut,
151
- updateUser,
152
- refreshSession,
153
- } = useAuth();
154
- }
155
- ```
156
-
157
- Features:
158
-
159
- - Tokens stored securely with expo-secure-store
160
- - Automatic refresh 5 minutes before expiry
161
- - Race condition handling for concurrent requests
162
- - Redirect to login on session expiry
163
-
164
- ## ๐Ÿ“ก API Client
165
-
166
- Robust HTTP client with automatic retry:
167
-
168
- ```tsx
169
- import { api } from "@/services/api";
170
-
171
- // Basic requests
172
- const users = await api.get<User[]>("/users");
173
- const user = await api.post<User>("/users", { name: "John" });
174
- await api.put("/users/1", { name: "Jane" });
175
- await api.delete("/users/1");
176
-
177
- // Skip auth for public endpoints
178
- await api.get("/public", { requiresAuth: false });
179
- ```
180
-
181
- ### 401 Handling
182
-
183
- The API client automatically:
184
-
185
- 1. Catches 401 responses
186
- 2. Refreshes the access token
187
- 3. Retries the original request
188
- 4. Redirects to login if refresh fails
189
-
190
- ## ๐Ÿ“Š Data Fetching
191
-
192
- TanStack Query with offline persistence:
193
-
194
- ```tsx
195
- import { useCurrentUser, useUpdateUser } from "@/hooks/useApi";
196
-
197
- function Profile() {
198
- const { data: user, isLoading, error } = useCurrentUser();
199
- const updateUser = useUpdateUser();
200
-
201
- const handleUpdate = () => {
202
- updateUser.mutate(
203
- { name: "New Name" },
204
- { onSuccess: () => toast.success("Updated!") }
205
- );
206
- };
207
- }
208
- ```
209
-
210
- ### CRUD Factory
211
-
212
- Create hooks for any resource:
213
-
214
- ```tsx
215
- import { createCrudHooks } from "@/hooks/useApi";
216
-
217
- const postsApi = createCrudHooks<Post>({
218
- baseKey: ["posts"],
219
- endpoint: "/posts",
220
- entityName: "Post",
221
- });
222
-
223
- // Usage
224
- const { data: posts } = postsApi.useList();
225
- const { data: post } = postsApi.useById("123");
226
- const createPost = postsApi.useCreate();
227
- ```
228
-
229
- ## ๐Ÿ“ด Offline Support
230
-
231
- Automatic offline handling:
232
-
233
- ```tsx
234
- import { useOffline } from "@/hooks/useOffline";
235
-
236
- function MyComponent() {
237
- const { isOffline, isOnline } = useOffline({ showToast: true });
238
- // Shows toast when connection lost/restored
239
- }
240
- ```
241
-
242
- Query cache persisted to AsyncStorage - data available offline.
243
-
244
- ## ๐ŸŽจ Skeleton Loaders
245
-
246
- Pre-built skeleton components:
247
-
248
- ```tsx
249
- import {
250
- Skeleton,
251
- SkeletonText,
252
- SkeletonCard,
253
- SkeletonProfile,
254
- SkeletonList,
255
- } from "@/components/ui/Skeleton";
256
-
257
- // Single skeleton
258
- <Skeleton width={200} height={20} />
259
-
260
- // Profile placeholder
261
- <SkeletonProfile />
262
-
263
- // List of cards
264
- <SkeletonList count={5} variant="card" />
265
- ```
266
-
267
- ## ๐Ÿ”” Toast Notifications
268
-
269
- Centralized toast system:
270
-
271
- ```tsx
272
- import { toast, handleApiError } from "@/utils/toast";
273
-
274
- // Simple toasts
275
- toast.success("Profile updated");
276
- toast.error("Something went wrong", "Please try again");
277
- toast.info("New message received");
278
-
279
- // Handle API errors automatically
280
- try {
281
- await api.post("/endpoint", data);
282
- } catch (error) {
283
- handleApiError(error); // Shows appropriate toast
284
- }
285
- ```
286
-
287
- ## ๐Ÿ›ก๏ธ Error Boundary
288
-
289
- Global error handling with Sentry:
290
-
291
- ```tsx
292
- // Already wrapped in _layout.tsx
293
- <ErrorBoundary>
294
- <App />
295
- </ErrorBoundary>;
296
-
297
- // Or use HOC for specific components
298
- import { withErrorBoundary } from "@/components/ErrorBoundary";
299
-
300
- const SafeComponent = withErrorBoundary(RiskyComponent);
301
- ```
302
-
303
- ## ๐Ÿ“‹ Form Validation
304
-
305
- React Hook Form + Zod:
306
-
307
- ```tsx
308
- import { useForm } from "react-hook-form";
309
- import { zodResolver } from "@hookform/resolvers/zod";
310
- import { FormInput } from "@/components/forms";
311
- import { loginSchema, LoginFormData } from "@/utils/validation";
312
-
313
- function LoginForm() {
314
- const { control, handleSubmit } = useForm<LoginFormData>({
315
- resolver: zodResolver(loginSchema),
316
- });
317
-
318
- return (
319
- <FormInput
320
- name="email"
321
- control={control}
322
- label="Email"
323
- keyboardType="email-address"
324
- />
325
- );
326
- }
327
- ```
328
-
329
- Pre-built schemas: `loginSchema`, `registerSchema`, `forgotPasswordSchema`, `profileSchema`
330
-
331
- ## ๐ŸŽญ Theming
332
-
333
- Dark/light mode with persistence:
334
-
335
- ```tsx
336
- import { useTheme } from "@/hooks/useTheme";
337
-
338
- function MyComponent() {
339
- const { isDark, mode, toggleTheme, setMode } = useTheme();
340
- // mode: 'light' | 'dark' | 'system'
341
- }
342
- ```
343
-
344
- ## ๐Ÿ”ง Configuration
345
-
346
- ### Environment Variables
347
-
348
- ```env
349
- # .env
350
- EXPO_PUBLIC_SENTRY_DSN=your-sentry-dsn
351
- ```
352
-
353
- ### Sentry Setup
354
-
355
- 1. Create project at [sentry.io](https://sentry.io)
356
- 2. Copy DSN to `.env`
357
- 3. Errors automatically reported in production
358
-
359
- ## ๐Ÿงช Testing
360
-
361
- 58+ tests included:
362
-
363
- ```bash
364
- npm test # Run all tests
365
- npm run test:watch # Watch mode
366
- npm run test:coverage # With coverage
367
- ```
368
-
369
- Test coverage:
370
-
371
- - `useAuth` hook - 24 tests
372
- - `ApiClient` - 22 tests
373
- - UI components - 12 tests
374
-
375
- ## ๐Ÿ“œ Available Scripts
376
-
377
- | Command | Description |
378
- | ----------------------- | ------------------------ |
379
- | `npm start` | Start Expo dev server |
380
- | `npm run ios` | Run on iOS simulator |
381
- | `npm run android` | Run on Android emulator |
382
- | `npm test` | Run tests |
383
- | `npm run storybook` | Start Storybook |
384
- | `npm run lint` | Run ESLint |
385
- | `npm run typecheck` | TypeScript check |
386
- | `npm run build:dev` | Build development client |
387
- | `npm run build:preview` | Build preview APK/IPA |
388
- | `npm run build:prod` | Build production release |
389
-
390
- ## โœ… Customization Checklist
391
-
392
- - [ ] Run init script or manually update placeholders
393
- - [ ] Replace icons in `assets/images/`
394
- - [ ] Configure API URL in `constants/config.ts`
395
- - [ ] Set up Sentry DSN in `.env`
396
- - [ ] Configure EAS: `eas build:configure`
397
- - [ ] Implement real API calls in `services/api.ts`
398
- - [ ] Add your analytics
399
-
400
- ## ๐Ÿ“„ License
401
-
402
- MIT
1
+ # @croacroa/react-native-template
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@croacroa/react-native-template.svg)](https://www.npmjs.com/package/@croacroa/react-native-template)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Expo SDK](https://img.shields.io/badge/Expo-SDK%2052-000020.svg)](https://expo.dev/)
7
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/croacroa-dev-team/template-react-native/pulls)
8
+
9
+ A production-ready React Native template with Expo SDK 52, featuring authentication, i18n, biometrics, offline support, and more.
10
+
11
+ ## โœจ Features
12
+
13
+ ### Core
14
+
15
+ - **Expo SDK 52** with TypeScript
16
+ - **Expo Router** for file-based navigation
17
+ - **NativeWind** (Tailwind CSS) for styling
18
+ - **Zustand** for state management
19
+ - **TanStack Query** with offline persistence
20
+ - **React Hook Form + Zod** for form validation
21
+
22
+ ### Authentication & Security
23
+
24
+ - **Auth Adapter Pattern** - Easy switching between Supabase, Firebase, etc.
25
+ - **Biometric Auth** - Face ID / Touch ID support
26
+ - **Secure token storage** with expo-secure-store
27
+ - **Automatic token refresh** with race condition handling
28
+
29
+ ### Internationalization
30
+
31
+ - **i18n** with expo-localization + i18next
32
+ - **English & French** translations included
33
+ - **Language detection** and persistence
34
+
35
+ ### UX Features
36
+
37
+ - **Dark/Light Theme** with system preference support
38
+ - **Onboarding Screens** with animated pagination
39
+ - **Push Notifications** with Expo Notifications
40
+ - **Toast Notifications** with Burnt
41
+ - **Deep Linking** support with route parsing
42
+ - **Skeleton Loaders** with shimmer animation
43
+ - **Offline Support** with connection status toasts
44
+ - **OTA Updates** with expo-updates integration
45
+
46
+ ### UI Components
47
+
48
+ - Button, Input, Card, Modal, Skeleton
49
+ - **Select/Dropdown**, Checkbox, Switch
50
+ - **BottomSheet** with @gorhom/bottom-sheet
51
+ - **Avatar** with initials fallback
52
+ - **Badge, Chip, CountBadge**
53
+ - **OptimizedImage** with expo-image
54
+
55
+ ### Animations & Transitions
56
+
57
+ - **Animation Presets** (timing, spring, bounce)
58
+ - **Screen Transitions** (slide, fade, modal) for Expo Router
59
+ - **useAnimatedEntry** & **useStaggeredEntry** hooks
60
+ - **useParallax** scroll effect hook
61
+ - **AnimatedScreen** & **AnimatedListItem** components
62
+
63
+ ### Permissions
64
+
65
+ - **Centralized Permission Manager** for all Expo permissions
66
+ - **usePermission** hook with auto-refresh on app resume
67
+ - **PermissionGate** component for declarative permission UI
68
+
69
+ ### Social Login
70
+
71
+ - **Google Sign-In** via expo-auth-session with PKCE
72
+ - **Apple Sign-In** via expo-apple-authentication (iOS)
73
+ - **SocialLoginButtons** component with platform-aware display
74
+
75
+ ### Analytics
76
+
77
+ - **Analytics Adapter Pattern** โ€” pluggable providers (PostHog, Mixpanel, etc.)
78
+ - **Auto screen tracking** via Expo Router
79
+ - **useTrackEvent** hook for custom events
80
+
81
+ ### Payments & Subscriptions
82
+
83
+ - **Payment Adapter Pattern** โ€” pluggable providers (RevenueCat, Stripe, etc.)
84
+ - **Paywall** component with product listing
85
+ - **useSubscription** hook for subscription status
86
+
87
+ ### File Upload & Media
88
+
89
+ - **Image picker** (camera + library) with compression
90
+ - **Upload with progress** tracking, cancel, retry
91
+ - **ImagePickerButton** & **UploadProgress** components
92
+
93
+ ### Real-time / WebSockets
94
+
95
+ - **WebSocketManager** with auto-reconnect & exponential backoff
96
+ - **useChannel** & **usePresence** hooks
97
+ - Offline queue & auth token injection
98
+
99
+ ### DevOps & Quality
100
+
101
+ - **GitHub Actions** CI/CD workflows
102
+ - **Maestro** E2E tests
103
+ - **Sentry** for crash reporting
104
+ - **Analytics Adapter** for multiple providers
105
+ - **Performance Monitoring** hooks
106
+ - **Accessibility** utilities and hooks
107
+ - **Jest + Testing Library** with 58+ tests
108
+ - **Storybook** for component documentation
109
+ - **ESLint + Prettier + Husky** for code quality
110
+
111
+ ## ๐Ÿš€ Quick Start
112
+
113
+ ### Option 1: Using npx (Recommended)
114
+
115
+ ```bash
116
+ npx create-expo-app my-app --template @croacroa/react-native-template
117
+ cd my-app
118
+ npm install
119
+ ```
120
+
121
+ ### Option 2: Using degit
122
+
123
+ ```bash
124
+ npx degit croacroa-dev-team/template-react-native my-app
125
+ cd my-app
126
+ ./scripts/init.sh # macOS/Linux
127
+ # or
128
+ .\scripts\init.ps1 # Windows PowerShell
129
+ ```
130
+
131
+ ### Option 3: Clone Repository
132
+
133
+ ```bash
134
+ git clone https://github.com/croacroa-dev-team/template-react-native my-app
135
+ cd my-app
136
+ rm -rf .git
137
+ npm install
138
+ cp .env.example .env
139
+ ```
140
+
141
+ Then update:
142
+
143
+ - `app.config.ts` - App name, bundle ID, scheme
144
+ - `package.json` - Package name
145
+ - `constants/config.ts` - API URLs
146
+
147
+ ### Run the App
148
+
149
+ ```bash
150
+ npm start # Start development server
151
+ npm run ios # Run on iOS simulator
152
+ npm run android # Run on Android emulator
153
+ ```
154
+
155
+ ## ๐Ÿ“ Project Structure
156
+
157
+ ```
158
+ โ”œโ”€โ”€ app/ # Expo Router pages
159
+ โ”‚ โ”œโ”€โ”€ (auth)/ # Protected routes (home, profile, settings)
160
+ โ”‚ โ”œโ”€โ”€ (public)/ # Public routes (login, register, forgot-password)
161
+ โ”‚ โ””โ”€โ”€ _layout.tsx # Root layout with providers
162
+ โ”œโ”€โ”€ components/
163
+ โ”‚ โ”œโ”€โ”€ ui/ # UI components (Button, Card, Modal, Skeleton)
164
+ โ”‚ โ”œโ”€โ”€ forms/ # Form components (FormInput)
165
+ โ”‚ โ””โ”€โ”€ ErrorBoundary.tsx # Global error handling
166
+ โ”œโ”€โ”€ hooks/ # useAuth, useTheme, useNotifications, useApi, useOffline
167
+ โ”œโ”€โ”€ stores/ # Zustand stores (appStore, notificationStore)
168
+ โ”œโ”€โ”€ services/
169
+ โ”‚ โ”œโ”€โ”€ api.ts # HTTP client with 401 retry
170
+ โ”‚ โ”œโ”€โ”€ queryClient.ts # TanStack Query with persistence
171
+ โ”‚ โ”œโ”€โ”€ sentry.ts # Crash reporting
172
+ โ”‚ โ””โ”€โ”€ storage.ts # AsyncStorage & SecureStore helpers
173
+ โ”œโ”€โ”€ utils/ # cn, toast, validation schemas
174
+ โ”œโ”€โ”€ constants/ # App configuration
175
+ โ”œโ”€โ”€ types/ # TypeScript types
176
+ โ”œโ”€โ”€ __tests__/ # Test files (58+ tests)
177
+ โ””โ”€โ”€ scripts/ # Init scripts for template setup
178
+ ```
179
+
180
+ ## ๐Ÿ” Authentication
181
+
182
+ Complete auth flow with automatic token refresh:
183
+
184
+ ```tsx
185
+ import { useAuth } from "@/hooks/useAuth";
186
+
187
+ function MyComponent() {
188
+ const {
189
+ user,
190
+ isAuthenticated,
191
+ isLoading,
192
+ signIn,
193
+ signUp,
194
+ signOut,
195
+ updateUser,
196
+ refreshSession,
197
+ } = useAuth();
198
+ }
199
+ ```
200
+
201
+ Features:
202
+
203
+ - Tokens stored securely with expo-secure-store
204
+ - Automatic refresh 5 minutes before expiry
205
+ - Race condition handling for concurrent requests
206
+ - Redirect to login on session expiry
207
+
208
+ ## ๐Ÿ“ก API Client
209
+
210
+ Robust HTTP client with automatic retry:
211
+
212
+ ```tsx
213
+ import { api } from "@/services/api";
214
+
215
+ // Basic requests
216
+ const users = await api.get<User[]>("/users");
217
+ const user = await api.post<User>("/users", { name: "John" });
218
+ await api.put("/users/1", { name: "Jane" });
219
+ await api.delete("/users/1");
220
+
221
+ // Skip auth for public endpoints
222
+ await api.get("/public", { requiresAuth: false });
223
+ ```
224
+
225
+ ### 401 Handling
226
+
227
+ The API client automatically:
228
+
229
+ 1. Catches 401 responses
230
+ 2. Refreshes the access token
231
+ 3. Retries the original request
232
+ 4. Redirects to login if refresh fails
233
+
234
+ ## ๐Ÿ“Š Data Fetching
235
+
236
+ TanStack Query with offline persistence:
237
+
238
+ ```tsx
239
+ import { useCurrentUser, useUpdateUser } from "@/hooks/useApi";
240
+
241
+ function Profile() {
242
+ const { data: user, isLoading, error } = useCurrentUser();
243
+ const updateUser = useUpdateUser();
244
+
245
+ const handleUpdate = () => {
246
+ updateUser.mutate(
247
+ { name: "New Name" },
248
+ { onSuccess: () => toast.success("Updated!") }
249
+ );
250
+ };
251
+ }
252
+ ```
253
+
254
+ ### CRUD Factory
255
+
256
+ Create hooks for any resource:
257
+
258
+ ```tsx
259
+ import { createCrudHooks } from "@/hooks/useApi";
260
+
261
+ const postsApi = createCrudHooks<Post>({
262
+ baseKey: ["posts"],
263
+ endpoint: "/posts",
264
+ entityName: "Post",
265
+ });
266
+
267
+ // Usage
268
+ const { data: posts } = postsApi.useList();
269
+ const { data: post } = postsApi.useById("123");
270
+ const createPost = postsApi.useCreate();
271
+ ```
272
+
273
+ ## ๐Ÿ“ด Offline Support
274
+
275
+ Automatic offline handling:
276
+
277
+ ```tsx
278
+ import { useOffline } from "@/hooks/useOffline";
279
+
280
+ function MyComponent() {
281
+ const { isOffline, isOnline } = useOffline({ showToast: true });
282
+ // Shows toast when connection lost/restored
283
+ }
284
+ ```
285
+
286
+ Query cache persisted to AsyncStorage - data available offline.
287
+
288
+ ## ๐ŸŽจ Skeleton Loaders
289
+
290
+ Pre-built skeleton components:
291
+
292
+ ```tsx
293
+ import {
294
+ Skeleton,
295
+ SkeletonText,
296
+ SkeletonCard,
297
+ SkeletonProfile,
298
+ SkeletonList,
299
+ } from "@/components/ui/Skeleton";
300
+
301
+ // Single skeleton
302
+ <Skeleton width={200} height={20} />
303
+
304
+ // Profile placeholder
305
+ <SkeletonProfile />
306
+
307
+ // List of cards
308
+ <SkeletonList count={5} variant="card" />
309
+ ```
310
+
311
+ ## ๐Ÿ”” Toast Notifications
312
+
313
+ Centralized toast system:
314
+
315
+ ```tsx
316
+ import { toast, handleApiError } from "@/utils/toast";
317
+
318
+ // Simple toasts
319
+ toast.success("Profile updated");
320
+ toast.error("Something went wrong", "Please try again");
321
+ toast.info("New message received");
322
+
323
+ // Handle API errors automatically
324
+ try {
325
+ await api.post("/endpoint", data);
326
+ } catch (error) {
327
+ handleApiError(error); // Shows appropriate toast
328
+ }
329
+ ```
330
+
331
+ ## ๐Ÿ›ก๏ธ Error Boundary
332
+
333
+ Global error handling with Sentry:
334
+
335
+ ```tsx
336
+ // Already wrapped in _layout.tsx
337
+ <ErrorBoundary>
338
+ <App />
339
+ </ErrorBoundary>;
340
+
341
+ // Or use HOC for specific components
342
+ import { withErrorBoundary } from "@/components/ErrorBoundary";
343
+
344
+ const SafeComponent = withErrorBoundary(RiskyComponent);
345
+ ```
346
+
347
+ ## ๐Ÿ“‹ Form Validation
348
+
349
+ React Hook Form + Zod:
350
+
351
+ ```tsx
352
+ import { useForm } from "react-hook-form";
353
+ import { zodResolver } from "@hookform/resolvers/zod";
354
+ import { FormInput } from "@/components/forms";
355
+ import { loginSchema, LoginFormData } from "@/utils/validation";
356
+
357
+ function LoginForm() {
358
+ const { control, handleSubmit } = useForm<LoginFormData>({
359
+ resolver: zodResolver(loginSchema),
360
+ });
361
+
362
+ return (
363
+ <FormInput
364
+ name="email"
365
+ control={control}
366
+ label="Email"
367
+ keyboardType="email-address"
368
+ />
369
+ );
370
+ }
371
+ ```
372
+
373
+ Pre-built schemas: `loginSchema`, `registerSchema`, `forgotPasswordSchema`, `profileSchema`
374
+
375
+ ## ๐ŸŽญ Theming
376
+
377
+ Dark/light mode with persistence:
378
+
379
+ ```tsx
380
+ import { useTheme } from "@/hooks/useTheme";
381
+
382
+ function MyComponent() {
383
+ const { isDark, mode, toggleTheme, setMode } = useTheme();
384
+ // mode: 'light' | 'dark' | 'system'
385
+ }
386
+ ```
387
+
388
+ ## ๐Ÿ”ง Configuration
389
+
390
+ ### Environment Variables
391
+
392
+ ```env
393
+ # .env
394
+ EXPO_PUBLIC_SENTRY_DSN=your-sentry-dsn
395
+ ```
396
+
397
+ ### Sentry Setup
398
+
399
+ 1. Create project at [sentry.io](https://sentry.io)
400
+ 2. Copy DSN to `.env`
401
+ 3. Errors automatically reported in production
402
+
403
+ ## ๐Ÿงช Testing
404
+
405
+ 58+ tests included:
406
+
407
+ ```bash
408
+ npm test # Run all tests
409
+ npm run test:watch # Watch mode
410
+ npm run test:coverage # With coverage
411
+ ```
412
+
413
+ Test coverage:
414
+
415
+ - `useAuth` hook - 24 tests
416
+ - `ApiClient` - 22 tests
417
+ - UI components - 12 tests
418
+
419
+ ## ๐Ÿ“œ Available Scripts
420
+
421
+ | Command | Description |
422
+ | ----------------------- | ------------------------ |
423
+ | `npm start` | Start Expo dev server |
424
+ | `npm run ios` | Run on iOS simulator |
425
+ | `npm run android` | Run on Android emulator |
426
+ | `npm test` | Run tests |
427
+ | `npm run storybook` | Start Storybook |
428
+ | `npm run lint` | Run ESLint |
429
+ | `npm run typecheck` | TypeScript check |
430
+ | `npm run build:dev` | Build development client |
431
+ | `npm run build:preview` | Build preview APK/IPA |
432
+ | `npm run build:prod` | Build production release |
433
+
434
+ ## โœ… Customization Checklist
435
+
436
+ - [ ] Run init script or manually update placeholders
437
+ - [ ] Replace icons in `assets/images/`
438
+ - [ ] Configure API URL in `constants/config.ts`
439
+ - [ ] Set up Sentry DSN in `.env`
440
+ - [ ] Configure EAS: `eas build:configure`
441
+ - [ ] Implement real API calls in `services/api.ts`
442
+ - [ ] Add your analytics
443
+
444
+ ## ๐Ÿ“„ License
445
+
446
+ MIT