create-react-native-starter-kit 0.0.0-stage → 1.0.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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +378 -2
  3. package/bin/create-react-native-starter-kit.js +95 -0
  4. package/lib/copy-src.js +127 -0
  5. package/lib/creator.js +211 -0
  6. package/lib/deps.js +58 -0
  7. package/lib/patch-configs.js +318 -0
  8. package/lib/patch-native.js +387 -0
  9. package/package.json +47 -4
  10. package/scripts/sync-template.js +52 -0
  11. package/template/.env.example +3 -0
  12. package/template/patches/react-native-element-dropdown+2.12.4.patch +67 -0
  13. package/template/src/app/App.tsx +25 -0
  14. package/template/src/app/navigation/root.tsx +24 -0
  15. package/template/src/app/navigation/stacks/app.stack.tsx +67 -0
  16. package/template/src/app/navigation/stacks/app.tab.tsx +72 -0
  17. package/template/src/app/navigation/stacks/auth.stack.tsx +56 -0
  18. package/template/src/app/providers.tsx +43 -0
  19. package/template/src/assets/fonts/InterTight-Black.ttf +0 -0
  20. package/template/src/assets/fonts/InterTight-BlackItalic.ttf +0 -0
  21. package/template/src/assets/fonts/InterTight-Bold.ttf +0 -0
  22. package/template/src/assets/fonts/InterTight-BoldItalic.ttf +0 -0
  23. package/template/src/assets/fonts/InterTight-ExtraBold.ttf +0 -0
  24. package/template/src/assets/fonts/InterTight-ExtraBoldItalic.ttf +0 -0
  25. package/template/src/assets/fonts/InterTight-ExtraLight.ttf +0 -0
  26. package/template/src/assets/fonts/InterTight-ExtraLightItalic.ttf +0 -0
  27. package/template/src/assets/fonts/InterTight-Italic.ttf +0 -0
  28. package/template/src/assets/fonts/InterTight-Light.ttf +0 -0
  29. package/template/src/assets/fonts/InterTight-LightItalic.ttf +0 -0
  30. package/template/src/assets/fonts/InterTight-Medium.ttf +0 -0
  31. package/template/src/assets/fonts/InterTight-MediumItalic.ttf +0 -0
  32. package/template/src/assets/fonts/InterTight-Regular.ttf +0 -0
  33. package/template/src/assets/fonts/InterTight-SemiBold.ttf +0 -0
  34. package/template/src/assets/fonts/InterTight-SemiBoldItalic.ttf +0 -0
  35. package/template/src/assets/fonts/InterTight-Thin.ttf +0 -0
  36. package/template/src/assets/fonts/InterTight-ThinItalic.ttf +0 -0
  37. package/template/src/assets/icons/back.svg +3 -0
  38. package/template/src/assets/icons/calendar.svg +3 -0
  39. package/template/src/assets/icons/claim_upload.svg +3 -0
  40. package/template/src/assets/icons/clock.svg +1 -0
  41. package/template/src/assets/icons/downArrow.svg +3 -0
  42. package/template/src/assets/icons/edit.svg +3 -0
  43. package/template/src/assets/icons/eye.svg +4 -0
  44. package/template/src/assets/icons/eyeClose.svg +7 -0
  45. package/template/src/assets/icons/folder.svg +5 -0
  46. package/template/src/assets/icons/home.svg +4 -0
  47. package/template/src/assets/icons/index.ts +33 -0
  48. package/template/src/assets/icons/setting.svg +4 -0
  49. package/template/src/assets/icons/tabs/home/filled_dark.svg +3 -0
  50. package/template/src/assets/icons/tabs/home/filled_light.svg +3 -0
  51. package/template/src/assets/icons/tabs/home/outline.svg +4 -0
  52. package/template/src/assets/icons/tabs/index.ts +31 -0
  53. package/template/src/assets/icons/tabs/profile/filled_dark.svg +6 -0
  54. package/template/src/assets/icons/tabs/profile/filled_light.svg +6 -0
  55. package/template/src/assets/icons/tabs/profile/outline.svg +3 -0
  56. package/template/src/assets/icons/tooltip_dark.svg +1 -0
  57. package/template/src/assets/icons/tooltip_light.svg +1 -0
  58. package/template/src/assets/icons/upArrow.svg +3 -0
  59. package/template/src/assets/images/back.png +0 -0
  60. package/template/src/assets/images/index.ts +7 -0
  61. package/template/src/assets/images/placeholder/profile.png +0 -0
  62. package/template/src/components/shared/AppImage.tsx +190 -0
  63. package/template/src/components/shared/AvatarPicker.tsx +175 -0
  64. package/template/src/components/shared/CustomButton.tsx +273 -0
  65. package/template/src/components/shared/DocumentPicker.tsx +86 -0
  66. package/template/src/components/shared/GlobalLoader.tsx +47 -0
  67. package/template/src/components/shared/Icon.tsx +28 -0
  68. package/template/src/components/shared/PressableIcon.tsx +104 -0
  69. package/template/src/components/shared/bottom-sheet/BaseBottomSheet.tsx +30 -0
  70. package/template/src/components/shared/bottom-sheet/BaseBottomSheetModal.tsx +50 -0
  71. package/template/src/components/shared/bottom-sheet/sheets/ImagePickerSheet.tsx +310 -0
  72. package/template/src/components/shared/bottom-sheet/types.ts +4 -0
  73. package/template/src/components/shared/form-input/FormInput.tsx +139 -0
  74. package/template/src/components/shared/form-input/FormInputRenderer.tsx +82 -0
  75. package/template/src/components/shared/form-input/FullNameInput.tsx +243 -0
  76. package/template/src/components/shared/form-input/formFieldStyles.ts +43 -0
  77. package/template/src/components/shared/form-input/type.ts +188 -0
  78. package/template/src/components/shared/form-input/variants/CountryPhoneVariant.tsx +289 -0
  79. package/template/src/components/shared/form-input/variants/DateTimeVariant.tsx +280 -0
  80. package/template/src/components/shared/form-input/variants/DropdownVariant.tsx +221 -0
  81. package/template/src/components/shared/form-input/variants/TextInputVariant.tsx +300 -0
  82. package/template/src/components/shared/form-input/variants/ToggleVariant.tsx +194 -0
  83. package/template/src/components/shared/modal/BaseModal.tsx +83 -0
  84. package/template/src/components/shared/modal/common/AlertModal.tsx +173 -0
  85. package/template/src/components/shared/navigation/AnimatedTabIcon.tsx +37 -0
  86. package/template/src/components/shared/navigation/HeaderActionButton.tsx +70 -0
  87. package/template/src/components/shared/navigation/HeaderBackButton.tsx +38 -0
  88. package/template/src/components/shared/navigation/StackHeader.tsx +105 -0
  89. package/template/src/components/shared/navigation/tabIconRender.tsx +14 -0
  90. package/template/src/components/shared/offline/OfflineQueryBoundary.tsx +25 -0
  91. package/template/src/components/shared/offline/OfflineState.tsx +229 -0
  92. package/template/src/components/shared/offline/index.ts +6 -0
  93. package/template/src/components/shared/offline/types.ts +15 -0
  94. package/template/src/components/shared/offline/wifi-icon-animation/BrokenWifiAnimation.tsx +116 -0
  95. package/template/src/components/shared/offline/wifi-icon-animation/WifiPiece.tsx +80 -0
  96. package/template/src/components/shared/offline/wifi-icon-animation/geometry.ts +163 -0
  97. package/template/src/components/shared/offline/wifi-icon-animation/useWifiIconAnimationActive.ts +37 -0
  98. package/template/src/components/shared/skelton/BaseSkelton.tsx +71 -0
  99. package/template/src/components/shared/skelton/ShimmerHolder.ts +6 -0
  100. package/template/src/components/shared/skelton/holders/HomeShimmer.tsx +25 -0
  101. package/template/src/components/shared/skelton/holders/index.ts +1 -0
  102. package/template/src/components/shared/skelton/layouts/avatar.layout.ts +18 -0
  103. package/template/src/components/shared/skelton/layouts/home.layout.ts +26 -0
  104. package/template/src/components/shared/skelton/layouts/index.ts +3 -0
  105. package/template/src/components/shared/skelton/layouts/text.layout.ts +18 -0
  106. package/template/src/components/shared/text/CustomText.tsx +85 -0
  107. package/template/src/components/shared/text/ShimmerText.tsx +159 -0
  108. package/template/src/components/shared/text/types.ts +40 -0
  109. package/template/src/constants/device.ts +12 -0
  110. package/template/src/constants/endpoint.ts +11 -0
  111. package/template/src/constants/keyExtractor.ts +6 -0
  112. package/template/src/constants/limits.ts +9 -0
  113. package/template/src/constants/queryKey.ts +3 -0
  114. package/template/src/constants/regex.ts +7 -0
  115. package/template/src/context/NetworkContext.tsx +102 -0
  116. package/template/src/context/ThemeContext.tsx +56 -0
  117. package/template/src/hooks/app/index.ts +2 -0
  118. package/template/src/hooks/app/useGetProfile.ts +38 -0
  119. package/template/src/hooks/app/useUpdateProfile.ts +21 -0
  120. package/template/src/hooks/useAppDispatch.ts +6 -0
  121. package/template/src/hooks/useAppSelector.ts +4 -0
  122. package/template/src/hooks/useDebounce.ts +15 -0
  123. package/template/src/hooks/useDocumentPicker.ts +102 -0
  124. package/template/src/hooks/useImagePicker.ts +83 -0
  125. package/template/src/i18n/i18next.d.ts +9 -0
  126. package/template/src/i18n/index.ts +16 -0
  127. package/template/src/i18n/locales/en.json +274 -0
  128. package/template/src/i18n/locales/ru.json +165 -0
  129. package/template/src/i18n/resources.ts +11 -0
  130. package/template/src/redux/selectors/auth.selectors.ts +4 -0
  131. package/template/src/redux/slice/auth.slice.ts +23 -0
  132. package/template/src/redux/slice/loader.slice.ts +25 -0
  133. package/template/src/redux/slice/settings.slice.ts +26 -0
  134. package/template/src/redux/slice/user.slice.ts +25 -0
  135. package/template/src/screens/app/main-tab/index.tsx +73 -0
  136. package/template/src/screens/app/main-tab/style.ts +0 -0
  137. package/template/src/screens/app/profile-setup/index.tsx +161 -0
  138. package/template/src/screens/app/profile-setup/styles.ts +3 -0
  139. package/template/src/screens/auth/forgot-password/index.tsx +59 -0
  140. package/template/src/screens/auth/forgot-password/styles.ts +0 -0
  141. package/template/src/screens/auth/index.ts +4 -0
  142. package/template/src/screens/auth/login/index.tsx +137 -0
  143. package/template/src/screens/auth/login/styles.ts +3 -0
  144. package/template/src/screens/auth/signup/index.tsx +110 -0
  145. package/template/src/screens/auth/signup/styles.ts +3 -0
  146. package/template/src/screens/auth/verify-email/index.tsx +15 -0
  147. package/template/src/screens/auth/verify-email/styles.ts +3 -0
  148. package/template/src/screens/tab/home/index.tsx +49 -0
  149. package/template/src/screens/tab/home/styles.ts +3 -0
  150. package/template/src/screens/tab/index.ts +2 -0
  151. package/template/src/screens/tab/profile/index.tsx +50 -0
  152. package/template/src/screens/tab/profile/styles.ts +3 -0
  153. package/template/src/services/api/auth/auth.api.ts +22 -0
  154. package/template/src/services/api/auth/auth.endpoints.ts +7 -0
  155. package/template/src/services/api/auth/auth.types.ts +9 -0
  156. package/template/src/services/api/auth/index.ts +3 -0
  157. package/template/src/services/api/axios/axios.instance.ts +15 -0
  158. package/template/src/services/api/axios/axios.interceptors.ts +43 -0
  159. package/template/src/services/api/axios/index.ts +2 -0
  160. package/template/src/services/api/mock/mockAdapter.ts +97 -0
  161. package/template/src/services/api/user/index.ts +2 -0
  162. package/template/src/services/api/user/user.api.ts +67 -0
  163. package/template/src/services/api/user/user.endpoints.ts +0 -0
  164. package/template/src/services/api/user/user.types.ts +0 -0
  165. package/template/src/services/errors/backendErrorCodes.ts +7 -0
  166. package/template/src/services/errors/errorHandler.ts +94 -0
  167. package/template/src/services/errors/firebaseErrorMap.ts +61 -0
  168. package/template/src/services/errors/httpStatusMap.ts +47 -0
  169. package/template/src/services/errors/index.ts +4 -0
  170. package/template/src/services/network/index.ts +1 -0
  171. package/template/src/services/network/onlineManager.ts +22 -0
  172. package/template/src/services/query/index.ts +1 -0
  173. package/template/src/services/query/queryClient.ts +23 -0
  174. package/template/src/services/toast/ToastCard.tsx +137 -0
  175. package/template/src/services/toast/ToastConfig.tsx +32 -0
  176. package/template/src/services/toast/index.ts +3 -0
  177. package/template/src/services/toast/toast.ts +68 -0
  178. package/template/src/storage/mmkvAdapter.ts +3 -0
  179. package/template/src/storage/mmkvInstance.ts +16 -0
  180. package/template/src/store/index.ts +15 -0
  181. package/template/src/store/mmkv.ts +6 -0
  182. package/template/src/store/persist.ts +10 -0
  183. package/template/src/store/rootReducer.ts +26 -0
  184. package/template/src/theme/dark.ts +40 -0
  185. package/template/src/theme/fonts.ts +20 -0
  186. package/template/src/theme/index.ts +6 -0
  187. package/template/src/theme/layout.ts +22 -0
  188. package/template/src/theme/light.ts +41 -0
  189. package/template/src/theme/palette.ts +32 -0
  190. package/template/src/theme/spacing.ts +7 -0
  191. package/template/src/theme/typography.ts +80 -0
  192. package/template/src/types/api.types.ts +34 -0
  193. package/template/src/types/env.d.ts +6 -0
  194. package/template/src/types/index.ts +2 -0
  195. package/template/src/types/navigation.types.ts +94 -0
  196. package/template/src/utils/clearAppSession.ts +10 -0
  197. package/template/src/utils/date.ts +19 -0
  198. package/template/src/utils/documentPickerHandler.ts +96 -0
  199. package/template/src/utils/getFileNameFromUri.ts +18 -0
  200. package/template/src/utils/getInitials.ts +13 -0
  201. package/template/src/utils/hindiText.ts +33 -0
  202. package/template/src/utils/imageCompress.ts +97 -0
  203. package/template/src/utils/imagePickerHandler.ts +200 -0
  204. package/template/src/utils/maskPhone.ts +17 -0
  205. package/template/src/utils/normalize.ts +12 -0
  206. package/template/src/validation/messages.ts +11 -0
  207. package/template/src/validation/rules.ts +44 -0
  208. package/template/src/validation/schema/auth.schema.ts +53 -0
  209. package/template/src/validation/schema/user.schema.ts +46 -0
  210. package/template/src/validation/tValError.ts +13 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Satyam Narayan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,379 @@
1
- # Temporary Holding Version
1
+ # create-react-native-starter-kit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/create-react-native-starter-kit.svg)](https://www.npmjs.com/package/create-react-native-starter-kit)
4
+ [![license](https://img.shields.io/npm/l/create-react-native-starter-kit.svg)](./LICENSE)
5
+ [![GitHub](https://img.shields.io/badge/GitHub-satyam--narayan-181717?logo=github)](https://github.com/satyam-narayan/create-react-native-starter-kit)
6
+ [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-FFDD00?logo=buymeacoffee&logoColor=black)](https://www.buymeacoffee.com/satyamnarayan)
7
+
8
+ Create a React Native CLI app that is ready for screen design on day one.
9
+
10
+ One command gives you a new React Native project with a ready folder structure, shared UI components, theming, i18n, navigation, an API layer with a built-in mock backend, offline handling, and all native (Android / iOS) setup already done.
11
+
12
+ ```bash
13
+ npx create-react-native-starter-kit MyApp
14
+ ```
15
+
16
+ ---
17
+
18
+ ## Table of contents
19
+
20
+ - [Requirements](#requirements)
21
+ - [Create a project](#create-a-project)
22
+ - [First run walkthrough](#first-run-walkthrough)
23
+ - [What the CLI does](#what-the-cli-does)
24
+ - [Folder structure](#folder-structure)
25
+ - [Included libraries](#included-libraries)
26
+ - [Shared components](#shared-components)
27
+ - [Important patterns](#important-patterns)
28
+ - [Screen data: OfflineQueryBoundary + shimmer](#1-screen-data-offlinequeryboundary--shimmer)
29
+ - [Blocking actions: global loader](#2-blocking-actions-global-loader)
30
+ - [API calls and the mock backend](#3-api-calls-and-the-mock-backend)
31
+ - [Query and mutation hooks](#4-query-and-mutation-hooks)
32
+ - [Toasts and errors](#5-toasts-and-errors)
33
+ - [Theme, fonts and icons](#6-theme-fonts-and-icons)
34
+ - [Translations](#7-translations)
35
+ - [Forms](#8-forms)
36
+ - [Environment variables](#environment-variables)
37
+ - [Useful scripts](#useful-scripts)
38
+ - [Troubleshooting](#troubleshooting)
39
+ - [Contributing](#contributing)
40
+ - [Support](#support)
41
+ - [License](#license)
42
+
43
+ ---
44
+
45
+ ## Requirements
46
+
47
+ Your machine must be set up for React Native CLI development (see the [official environment setup](https://reactnative.dev/docs/set-up-your-environment)):
48
+
49
+ - Node.js 22.13 or newer (required by the latest React Native; check with `node -v`)
50
+ - Yarn (default) or npm
51
+ - Android Studio, Android SDK and JDK 17 for Android
52
+ - Xcode and CocoaPods for iOS (macOS only)
53
+ - Internet connection (the React Native template and all libraries are downloaded from npm)
54
+
55
+ > **Note:** If you previously installed a global `react-native-cli` or `@react-native-community/cli` package, remove it first, as recommended in the [React Native docs](https://reactnative.dev/docs/getting-started-without-a-framework). A global install can cause unexpected issues.
56
+ >
57
+ > ```bash
58
+ > npm uninstall -g react-native-cli @react-native-community/cli
59
+ > ```
60
+ >
61
+ > This CLI creates the base app with `npx @react-native-community/cli@latest init`, the same command the docs use, so you always start on the latest stable React Native.
62
+
63
+ ## Create a project
64
+
65
+ ```bash
66
+ # with npx (recommended)
67
+ npx create-react-native-starter-kit MyApp
68
+
69
+ # or install globally
70
+ npm install -g create-react-native-starter-kit
71
+ create-react-native-starter-kit MyApp
72
+ ```
73
+
74
+ Run without a name to be asked for one.
75
+
76
+ | Option | Description |
77
+ | --- | --- |
78
+ | `--npm` | Use npm instead of yarn |
79
+ | `--yarn` | Use yarn (default) |
80
+ | `--skip-install` | Create files only, do not install packages or pods |
81
+ | `--skip-pods` | Skip `pod install` on macOS |
82
+ | `--dry-run` | Print the steps without writing anything |
83
+ | `-h`, `--help` | Show help |
84
+
85
+ The project name must start with a letter and contain only letters, numbers or underscores (`MyApp`, `my_app`).
86
+
87
+ Then run it:
88
+
89
+ ```bash
90
+ cd MyApp
91
+ yarn android
92
+ yarn ios
93
+ ```
94
+
95
+ ### First run walkthrough
96
+
97
+ Everything in the generated app is a working **reference**. No backend or real account is needed, so you can click through all screens right away.
98
+
99
+ 1. **Login** — type any email and password that pass the form validation, then tap Login. Nothing is sent to a server; a dummy token is saved and you are logged in.
100
+ - Email: any valid format, e.g. `test@example.com`
101
+ - Password: 8–16 characters with an uppercase letter, a lowercase letter, a number and a special character, e.g. `Test@1234`
102
+ 2. **Profile Setup** — shown once after the first login. Fill every field with anything you like (name, phone, gender, date of birth, time, address) and tap Continue. The values are only logged to the console; the screen exists to show every `FormInput` variant in use.
103
+ 3. **Bottom tabs**
104
+ - **Home** — `OfflineQueryBoundary` + shimmer reference and the `AvatarPicker` with the camera / gallery sheet.
105
+ - **Profile** — global loader reference (shows for 2 seconds on first open). "Go to Main Tab" opens a screen with **Logout**, which uses the `AlertModal` confirm dialog and returns you to Login.
106
+
107
+ The login state is persisted (Redux + MMKV), so after a restart you stay logged in until you log out.
108
+
109
+ Replace the dummy parts with your real logic when you are ready: `handleLogin` in `screens/auth/login`, `handleContinue` in `screens/app/profile-setup`, and the API functions in `services/api`.
110
+
111
+ ## What the CLI does
112
+
113
+ 1. Creates a bare app with `@react-native-community/cli init` (latest stable React Native).
114
+ 2. Copies the starter `src/`, `patches/` and `.env.example` into it.
115
+ 3. Installs the starter libraries (latest versions).
116
+ 4. Writes `babel.config.js` (`@/` alias, env variables, worklets), `metro.config.js` (SVG as components), `tsconfig.json` paths, `react-native.config.js` (fonts), `declarations.d.ts` and `App.tsx`.
117
+ 5. Applies the native setup:
118
+ - Android: `MainActivity` (screens fragment factory), permissions in `AndroidManifest.xml`, `build.gradle`, Gradle network timeout.
119
+ - iOS: permission texts and fonts in `Info.plist`, Podfile deployment target.
120
+ 6. Links the fonts and generates the icon map.
121
+ 7. Runs `pod install` on macOS.
122
+
123
+ ## Folder structure
124
+
125
+ ```text
126
+ src/
127
+ ├── app/ App.tsx, providers, navigation (root, auth stack, app stack, bottom tabs)
128
+ ├── assets/
129
+ │ ├── fonts/ InterTight font family (linked automatically)
130
+ │ ├── icons/ SVG icons + generated index.ts (Icons map, IconName type)
131
+ │ └── images/ Static images (placeholder avatar, etc.)
132
+ ├── components/shared/ Reusable UI (see "Shared components")
133
+ ├── constants/ device sizes, API endpoints, query keys, limits, regex
134
+ ├── context/ ThemeContext (light / dark), NetworkContext (online / offline)
135
+ ├── hooks/ useAppDispatch, useAppSelector, useDebounce, useImagePicker, useDocumentPicker
136
+ │ └── app/ API hooks: useGetProfile (query), useUpdateProfile (mutation)
137
+ ├── i18n/ i18next setup and locales (en, ru)
138
+ ├── redux/ slices (auth, user, settings, loader) and selectors
139
+ ├── screens/ auth/, app/, tab/ — one folder per screen with index.tsx + styles.ts
140
+ ├── services/
141
+ │ ├── api/ axios instance + interceptors, mock backend, feature APIs (auth, user)
142
+ │ ├── errors/ ErrorHandler and status / backend error maps
143
+ │ ├── network/ TanStack Query online manager
144
+ │ ├── query/ QueryClient
145
+ │ └── toast/ Success / Error / Info toasts and custom ToastCard
146
+ ├── storage/ store/ MMKV storage, Redux store with redux-persist
147
+ ├── theme/ palette, light / dark colors, spacing, typography, fonts, layout helpers
148
+ ├── types/ API, navigation and env types
149
+ ├── utils/ normalize, date, image compress, pickers, initials, masking
150
+ └── validation/ yup rules, schemas and translated messages
151
+ ```
152
+
153
+ Import anything from `src` with the `@/` alias:
154
+
155
+ ```tsx
156
+ import CustomButton from '@/components/shared/CustomButton';
157
+ import { useAppTheme } from '@/context/ThemeContext';
158
+ ```
159
+
160
+ ## Included libraries
161
+
162
+ | Area | Library |
163
+ | --- | --- |
164
+ | Navigation | `@react-navigation/native`, `native-stack`, `bottom-tabs`, `react-native-screens`, `react-native-safe-area-context` |
165
+ | State | `@reduxjs/toolkit`, `react-redux`, `redux-persist`, `react-native-mmkv` |
166
+ | Server data | `@tanstack/react-query`, `axios`, `@react-native-community/netinfo` |
167
+ | Forms | `react-hook-form`, `yup`, `@hookform/resolvers` |
168
+ | UI | `@gorhom/bottom-sheet`, `react-native-reanimated`, `react-native-gesture-handler`, `react-native-svg`, `react-native-linear-gradient`, `react-native-reanimated-skeleton`, `react-native-toast-message`, `@d11/react-native-fast-image` |
169
+ | Inputs | `react-native-element-dropdown`, `@react-native-community/datetimepicker`, `rn-international-phone-number`, `react-native-walkthrough-tooltip` |
170
+ | Media | `react-native-image-picker`, `@react-native-documents/picker`, `@bam.tech/react-native-image-resizer` |
171
+ | Other | `i18next`, `react-i18next`, `moment`, `@mhpdev/react-native-haptics` |
172
+
173
+ ## Shared components
174
+
175
+ All live in `src/components/shared/`.
176
+
177
+ | Component | Use it for |
178
+ | --- | --- |
179
+ | `text/CustomText` | Every text. Props: `variant` (`display`, `header`, `title`, `titleMd`, `titleSm`, `body`, `callout`, `label`, `subtitle`, `footnote`, `caption`, `micro`), `weight`, `textColor`, `textAlign` |
180
+ | `text/ShimmerText` | Text that shows a shimmer while `isLoading` |
181
+ | `CustomButton` | Buttons with variants, icons, `loading`, haptics and `requiresNetwork` (shows an offline toast instead of running) |
182
+ | `PressableIcon` | Tappable icon with haptic feedback, press animation and double-tap protection |
183
+ | `Icon` | Render any SVG from `assets/icons` by `name` |
184
+ | `AppImage` | Fast image with loader, placeholder, initials or icon fallback |
185
+ | `AvatarPicker` | Avatar with edit button that opens the camera / gallery sheet |
186
+ | `DocumentPicker` | Ref-based picker for files or camera |
187
+ | `form-input/FormInput` | react-hook-form field with variants: `text`, `dropdown`, `date`, `time`, `country-phone`, `toggle` |
188
+ | `form-input/FullNameInput` | First / last name pair |
189
+ | `bottom-sheet/BaseBottomSheet`, `BaseBottomSheetModal` | Themed bottom sheets controlled by a ref (`open`, `close`) |
190
+ | `bottom-sheet/sheets/ImagePickerSheet` | Ready camera / gallery chooser |
191
+ | `modal/BaseModal` | Centered modal with overlay |
192
+ | `modal/common/AlertModal` | Confirm / alert dialog with icon, title, description and two buttons |
193
+ | `navigation/StackHeader`, `HeaderBackButton`, `HeaderActionButton` | Custom stack headers |
194
+ | `navigation/AnimatedTabIcon`, `tabIconRender` | Animated bottom tab icons |
195
+ | `offline/OfflineQueryBoundary` | Chooses between content, loading UI and offline UI (see below) |
196
+ | `offline/OfflineState` | Full offline screen with animated broken-wifi icon and retry |
197
+ | `skelton/BaseSkelton` | Themed skeleton wrapper |
198
+ | `skelton/ShimmerHolder` | Registry of full-screen shimmers (`ShimmerHolder.Home`) |
199
+ | `GlobalLoader` | Full-screen loader driven by Redux (`showLoader` / `hideLoader`) |
200
+
201
+ ## Important patterns
202
+
203
+ ### 1. Screen data: OfflineQueryBoundary + shimmer
204
+
205
+ Wrap the content of any screen that loads data. It decides what to render:
206
+
207
+ | Has data? | Network | Renders |
208
+ | --- | --- | --- |
209
+ | yes | online or offline | your content (cached data still shows offline) |
210
+ | no | online | `loading` (a shimmer) |
211
+ | no | offline | `OfflineState` with retry |
212
+
213
+ ```tsx
214
+ import { OfflineQueryBoundary } from '@/components/shared/offline';
215
+ import { ShimmerHolder } from '@/components/shared/skelton/ShimmerHolder';
216
+ import { useGetProfile } from '@/hooks/app';
217
+
218
+ const Home = () => {
219
+ const { data } = useGetProfile();
220
+
221
+ return (
222
+ <OfflineQueryBoundary hasData={!!data} loading={<ShimmerHolder.Home />}>
223
+ {/* screen content */}
224
+ </OfflineQueryBoundary>
225
+ );
226
+ };
227
+ ```
228
+
229
+ Try it: in `src/screens/tab/home/index.tsx` set `HAS_DATA = false`. With internet on you see the shimmer; turn on airplane mode and you see the offline screen.
230
+
231
+ **Add a shimmer for a new screen** (same three steps every time):
232
+
233
+ 1. `skelton/layouts/<screen>.layout.ts` — return the blocks (width, height, borderRadius, margins) that match your screen.
234
+ 2. `skelton/holders/<Screen>Shimmer.tsx` — render `BaseSkelton` with that layout; export it from `holders/index.ts`.
235
+ 3. Register it in `skelton/ShimmerHolder.ts` and use `<ShimmerHolder.<Screen> />`.
236
+
237
+ ### 2. Blocking actions: global loader
238
+
239
+ Use the global loader when the user must wait for an action (submit, upload, delete). For first-time screen data, use a shimmer instead.
240
+
241
+ ```tsx
242
+ import { useAppDispatch } from '@/hooks/useAppDispatch';
243
+ import { hideLoader, showLoader } from '@/redux/slice/loader.slice';
244
+
245
+ const dispatch = useAppDispatch();
246
+
247
+ const onSave = async () => {
248
+ dispatch(showLoader());
249
+ try {
250
+ await updateProfile(values);
251
+ } finally {
252
+ dispatch(hideLoader());
253
+ }
254
+ };
255
+ ```
256
+
257
+ See `src/screens/tab/profile/index.tsx` for a running example.
258
+
259
+ ### 3. API calls and the mock backend
260
+
261
+ - `services/api/axios` — one `apiClient` with base URL, timeout, auth token and error interceptors.
262
+ - `constants/endpoint.ts` — all endpoint paths.
263
+ - `services/api/<feature>/` — plain async functions per feature (`getProfile`, `updateProfile`, `login`, ...). They unwrap the standard response `{ result, message, payload }`.
264
+
265
+ While `API_BASE_URL` is **not** set in `.env`, every request is answered by `services/api/mock/mockAdapter.ts` after a short delay. You can build and click through every screen without a server. To add a fake endpoint, add a handler there:
266
+
267
+ ```ts
268
+ [`get ${ENDPOINTS.app.orders}`]: () => ({
269
+ message: 'Orders loaded',
270
+ payload: [{ id: 1, title: 'First order' }],
271
+ }),
272
+ ```
273
+
274
+ When your backend is ready, set `API_BASE_URL` in `.env` and restart Metro with `yarn start --reset-cache`. No screen code changes.
275
+
276
+ ### 4. Query and mutation hooks
277
+
278
+ Screens never call axios directly. They use hooks from `src/hooks/app/`:
279
+
280
+ - `useGetProfile()` — `useQuery` with a key from `constants/queryKey.ts`, caches the profile and mirrors it into Redux.
281
+ - `useUpdateProfile()` — `useMutation` that uploads (compressing images), shows a success toast and invalidates the profile query so every screen refreshes.
282
+
283
+ Copy these two files as the template for every new feature.
284
+
285
+ ### 5. Toasts and errors
286
+
287
+ ```ts
288
+ import { SuccessToast, ErrorToast, InfoToast } from '@/services/toast';
289
+ import { ErrorHandler } from '@/services/errors';
290
+
291
+ SuccessToast({ message: 'Saved' });
292
+ ErrorHandler(error); // maps HTTP / backend errors to a readable toast
293
+ ```
294
+
295
+ Going offline shows a toast automatically (`NetworkContext`). Read the network state anywhere with `useNetwork()` (`isOffline`, `refresh`).
296
+
297
+ ### 6. Theme, fonts and icons
298
+
299
+ ```tsx
300
+ const { color, mode, setMode } = useAppTheme(); // mode: 'light' | 'dark' | 'system'
301
+ <View style={{ backgroundColor: color.background_primary }} />
302
+ ```
303
+
304
+ - Colors: `theme/light.ts` and `theme/dark.ts` (same token names), built from `theme/palette.ts`.
305
+ - Spacing and layout helpers: `theme/spacing.ts`, `theme/layout.ts`.
306
+ - Fonts: InterTight, used through `CustomText` `weight`.
307
+ - Responsive sizes: `normalize()` from `utils/normalize`.
308
+ - Icons: drop an `.svg` into `src/assets/icons`, run `yarn generate:icons`, then `<Icon name="my-icon" size={20} color={color.primary} />`.
309
+
310
+ ### 7. Translations
311
+
312
+ Add keys to `src/i18n/locales/en.json` (and other locales), then:
313
+
314
+ ```tsx
315
+ const { t } = useTranslation();
316
+ <CustomText>{t('profile.editProfile')}</CustomText>
317
+ ```
318
+
319
+ Keys are type-checked.
320
+
321
+ ### 8. Forms
322
+
323
+ ```tsx
324
+ const { control, handleSubmit, formState: { errors } } = useForm({
325
+ resolver: yupResolver(loginSchema),
326
+ });
327
+
328
+ <FormInput control={control} name="email" label="Email" variant="text" error={errors.email?.message} />
329
+ ```
330
+
331
+ Schemas live in `src/validation/schema/`, shared rules and translated messages in `src/validation/`. See the Login and Sign up screens.
332
+
333
+ ## Environment variables
334
+
335
+ `.env.example` is copied into the project. Create `.env` next to it:
336
+
337
+ ```bash
338
+ # leave unset to use the mock backend
339
+ API_BASE_URL=https://api.yourserver.com
340
+ ```
341
+
342
+ Values are inlined at build time (`process.env.API_BASE_URL`). Restart Metro with `--reset-cache` after changing them. `.env` is git-ignored.
343
+
344
+ ## Useful scripts
345
+
346
+ | Command | What it does |
347
+ | --- | --- |
348
+ | `yarn android` / `yarn ios` | Run the app |
349
+ | `yarn start --reset-cache` | Start Metro with a clean cache (after `.env` or babel changes) |
350
+ | `yarn generate:icons` | Rebuild `src/assets/icons/index.ts` from the SVG files |
351
+ | `npx react-native-asset` | Re-link fonts after adding new ones to `src/assets/fonts` |
352
+
353
+ ## Troubleshooting
354
+
355
+ | Problem | Fix |
356
+ | --- | --- |
357
+ | `init` fails or creates an old React Native version | Remove global CLIs: `npm uninstall -g react-native-cli @react-native-community/cli`, then run again |
358
+ | Node version error during `init` or install | Upgrade Node to 22.13 or newer (`node -v`) |
359
+ | `pod install` failed at the end | `cd ios && bundle install && bundle exec pod install` |
360
+ | `patch-package` warning about `react-native-element-dropdown` | A newer version of that library was installed. The app still works; delete the old file in `patches/` or recreate it with `npx patch-package react-native-element-dropdown` |
361
+ | `@/...` import or `.env` value not found | Restart Metro with a clean cache: `yarn start --reset-cache` |
362
+ | Fonts missing after adding new ones | `npx react-native-asset`, then rebuild the app |
363
+ | Android build cannot download Gradle | Check your network / proxy and run `cd android && ./gradlew clean`, then build again |
364
+
365
+ ---
366
+
367
+ ## Contributing
368
+
369
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/satyam-narayan/create-react-native-starter-kit/issues). See [CONTRIBUTING.md](https://github.com/satyam-narayan/create-react-native-starter-kit/blob/main/CONTRIBUTING.md) for the project layout and how to test changes locally.
370
+
371
+ ## Support
372
+
373
+ If this starter kit saves you time, you can support its development:
374
+
375
+ <a href="https://www.buymeacoffee.com/satyamnarayan" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me a Coffee" height="50"></a>
376
+
377
+ ## License
378
+
379
+ [MIT](./LICENSE) © [Satyam Narayan](https://github.com/satyam-narayan)
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ const readline = require('readline');
4
+ const { createProject } = require('../lib/creator');
5
+
6
+ function showHelp() {
7
+ console.log(`
8
+ Usage: create-react-native-starter-kit <ProjectName> [options]
9
+
10
+ Arguments:
11
+ <ProjectName> Name of the new React Native project (e.g. MyApp)
12
+
13
+ Options:
14
+ --npm Use npm as the package manager (default is yarn)
15
+ --yarn Use yarn as the package manager (default)
16
+ --skip-install Skip npm/yarn install and pod install
17
+ --skip-pods Skip CocoaPods pod install (macOS only)
18
+ --dry-run Simulate the creation process without writing files
19
+ -h, --help Display this help message
20
+
21
+ Examples:
22
+ npx create-react-native-starter-kit MyApp
23
+ npx create-react-native-starter-kit MyApp --npm
24
+ npx create-react-native-starter-kit MyApp --skip-install
25
+ `);
26
+ }
27
+
28
+ async function promptProjectName() {
29
+ const rl = readline.createInterface({
30
+ input: process.stdin,
31
+ output: process.stdout,
32
+ });
33
+
34
+ return new Promise(resolve => {
35
+ rl.question('? Enter your project name (e.g. MyApp): ', answer => {
36
+ rl.close();
37
+ resolve(answer.trim());
38
+ });
39
+ });
40
+ }
41
+
42
+ async function main() {
43
+ const args = process.argv.slice(2);
44
+
45
+ if (args.includes('-h') || args.includes('--help')) {
46
+ showHelp();
47
+ process.exit(0);
48
+ }
49
+
50
+ let projectName = null;
51
+ const options = {
52
+ packageManager: 'yarn',
53
+ skipInstall: false,
54
+ skipPods: false,
55
+ dryRun: false,
56
+ };
57
+
58
+ for (const arg of args) {
59
+ if (arg === '--npm') {
60
+ options.packageManager = 'npm';
61
+ } else if (arg === '--yarn') {
62
+ options.packageManager = 'yarn';
63
+ } else if (arg === '--skip-install') {
64
+ options.skipInstall = true;
65
+ } else if (arg === '--skip-pods') {
66
+ options.skipPods = true;
67
+ } else if (arg === '--dry-run') {
68
+ options.dryRun = true;
69
+ } else if (!arg.startsWith('-') && !projectName) {
70
+ projectName = arg;
71
+ }
72
+ }
73
+
74
+ if (!projectName) {
75
+ projectName = await promptProjectName();
76
+ }
77
+
78
+ if (!projectName) {
79
+ console.error('❌ Error: Project name is required.');
80
+ process.exit(1);
81
+ }
82
+
83
+ try {
84
+ await createProject({
85
+ projectName,
86
+ ...options,
87
+ });
88
+ } catch (error) {
89
+ console.error('\n❌ Failed to create project:');
90
+ console.error(error.message);
91
+ process.exit(1);
92
+ }
93
+ }
94
+
95
+ main();
@@ -0,0 +1,127 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Recursively copy a directory, ignoring unwanted system files
6
+ */
7
+ function copyDirectoryRecursive(src, dest) {
8
+ if (!fs.existsSync(src)) {
9
+ throw new Error(`Source directory does not exist: ${src}`);
10
+ }
11
+
12
+ if (!fs.existsSync(dest)) {
13
+ fs.mkdirSync(dest, { recursive: true });
14
+ }
15
+
16
+ const entries = fs.readdirSync(src, { withFileTypes: true });
17
+
18
+ for (const entry of entries) {
19
+ if (entry.name === '.DS_Store' || entry.name === 'Thumbs.db') {
20
+ continue;
21
+ }
22
+
23
+ const srcPath = path.join(src, entry.name);
24
+ const destPath = path.join(dest, entry.name);
25
+
26
+ if (entry.isDirectory()) {
27
+ copyDirectoryRecursive(srcPath, destPath);
28
+ } else {
29
+ fs.copyFileSync(srcPath, destPath);
30
+ }
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Recursively find all files in a directory
36
+ */
37
+ function getAllFiles(dir, fileList = []) {
38
+ if (!fs.existsSync(dir)) return fileList;
39
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
40
+ for (const entry of entries) {
41
+ const fullPath = path.join(dir, entry.name);
42
+ if (entry.isDirectory()) {
43
+ getAllFiles(fullPath, fileList);
44
+ } else {
45
+ fileList.push(fullPath);
46
+ }
47
+ }
48
+ return fileList;
49
+ }
50
+
51
+ /**
52
+ * Sanitize all source files to prevent accidental 'node_modules/' prefixes in imports
53
+ */
54
+ function sanitizeSourceFiles(targetSrcDir) {
55
+ if (!fs.existsSync(targetSrcDir)) return;
56
+
57
+ const files = getAllFiles(targetSrcDir);
58
+ let sanitizedCount = 0;
59
+
60
+ for (const filePath of files) {
61
+ if (!/\.(tsx?|jsx?)$/.test(filePath)) continue;
62
+
63
+ const content = fs.readFileSync(filePath, 'utf8');
64
+ if (content.includes('node_modules/')) {
65
+ // Replaces 'node_modules/package-name' -> 'package-name'
66
+ const sanitized = content.replace(/(['"])node_modules\//g, '$1');
67
+ if (sanitized !== content) {
68
+ fs.writeFileSync(filePath, sanitized, 'utf8');
69
+ sanitizedCount++;
70
+ }
71
+ }
72
+ }
73
+
74
+ if (sanitizedCount > 0) {
75
+ console.log(`🧹 Sanitized ${sanitizedCount} file(s) with clean module import paths.`);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Copy starter src directory to target project and sanitize imports
81
+ */
82
+ function copySrcFolder(starterRoot, targetDir) {
83
+ const sourceSrc = path.join(starterRoot, 'src');
84
+ const targetSrc = path.join(targetDir, 'src');
85
+
86
+ if (!fs.existsSync(sourceSrc)) {
87
+ throw new Error(`Starter src directory not found at: ${sourceSrc}`);
88
+ }
89
+
90
+ console.log('📦 Copying src/ directory into target project...');
91
+ copyDirectoryRecursive(sourceSrc, targetSrc);
92
+ sanitizeSourceFiles(targetSrc);
93
+ console.log('✅ Successfully copied and validated src/ directory');
94
+ }
95
+
96
+
97
+ /**
98
+ * Copy starter patches directory to target project if it exists
99
+ */
100
+ function copyPatchesFolder(starterRoot, targetDir) {
101
+ const sourcePatches = path.join(starterRoot, "patches");
102
+ const targetPatches = path.join(targetDir, "patches");
103
+
104
+ if (fs.existsSync(sourcePatches)) {
105
+ console.log("📦 Copying patches/ directory into target project...");
106
+ copyDirectoryRecursive(sourcePatches, targetPatches);
107
+ console.log("✅ Successfully copied patches/ directory");
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Copy the starter .env.example (documents the mock API switch) into the target project
113
+ */
114
+ function copyEnvExample(starterRoot, targetDir) {
115
+ const source = path.join(starterRoot, '.env.example');
116
+ if (fs.existsSync(source)) {
117
+ fs.copyFileSync(source, path.join(targetDir, '.env.example'));
118
+ }
119
+ }
120
+
121
+ module.exports = {
122
+ copySrcFolder,
123
+ copyPatchesFolder,
124
+ copyEnvExample,
125
+ copyDirectoryRecursive,
126
+ sanitizeSourceFiles,
127
+ };