create-react-native-starter-kit
v1.0.2
Published
Create a React Native CLI app with a ready folder structure, shared components, theming, i18n, API layer, offline handling and native setup done for you
Maintainers
Readme
create-react-native-starter-kit
Create a React Native CLI app that is ready for screen design on day one.
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.
npx create-react-native-starter-kit MyAppTable of contents
- Requirements
- Create a project
- What the CLI does
- Folder structure
- Included libraries
- Shared components
- Important patterns
- Environment variables
- Useful scripts
- Troubleshooting
- Contributing
- Support
- License
Requirements
Your machine must be set up for React Native CLI development (see the official environment setup):
- Node.js 22.13 or newer (required by the latest React Native; check with
node -v) - Yarn (default) or npm
- Android Studio, Android SDK and JDK 17 for Android
- Xcode and CocoaPods for iOS (macOS only)
- Internet connection (the React Native template and all libraries are downloaded from npm)
Note: If you previously installed a global
react-native-clior@react-native-community/clipackage, remove it first, as recommended in the React Native docs. A global install can cause unexpected issues.npm uninstall -g react-native-cli @react-native-community/cliThis 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.
Create a project
# with npx (recommended)
npx create-react-native-starter-kit MyApp
# or install globally
npm install -g create-react-native-starter-kit
create-react-native-starter-kit MyAppRun without a name to be asked for one.
| Option | Description |
| --- | --- |
| --npm | Use npm instead of yarn |
| --yarn | Use yarn (default) |
| --skip-install | Create files only, do not install packages or pods |
| --skip-pods | Skip pod install on macOS |
| --dry-run | Print the steps without writing anything |
| -h, --help | Show help |
The project name must start with a letter and contain only letters, numbers or underscores (MyApp, my_app).
Then run it:
cd MyApp
yarn android
yarn iosFirst run walkthrough
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.
- 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.
- Email: any valid format, e.g.
[email protected] - Password: 8–16 characters with an uppercase letter, a lowercase letter, a number and a special character, e.g.
Test@1234
- Email: any valid format, e.g.
- 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
FormInputvariant in use. - Bottom tabs
- Home —
OfflineQueryBoundary+ shimmer reference and theAvatarPickerwith the camera / gallery sheet. - Profile — global loader reference (shows for 2 seconds on first open). "Go to Main Tab" opens a screen with Logout, which uses the
AlertModalconfirm dialog and returns you to Login.
- Home —
The login state is persisted (Redux + MMKV), so after a restart you stay logged in until you log out.
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.
What the CLI does
- Creates a bare app with
@react-native-community/cli init(latest stable React Native). - Copies the starter
src/,patches/and.env.exampleinto it. - Installs the starter libraries (latest versions).
- Writes
babel.config.js(@/alias, env variables, worklets),metro.config.js(SVG as components),tsconfig.jsonpaths,react-native.config.js(fonts),declarations.d.tsandApp.tsx. - Applies the native setup:
- Android:
MainActivity(screens fragment factory), permissions inAndroidManifest.xml,build.gradle, Gradle network timeout. - iOS: permission texts and fonts in
Info.plist, Podfile deployment target.
- Android:
- Links the fonts and generates the icon map.
- Runs
pod installon macOS.
Folder structure
src/
├── app/ App.tsx, providers, navigation (root, auth stack, app stack, bottom tabs)
├── assets/
│ ├── fonts/ InterTight font family (linked automatically)
│ ├── icons/ SVG icons + generated index.ts (Icons map, IconName type)
│ └── images/ Static images (placeholder avatar, etc.)
├── components/shared/ Reusable UI (see "Shared components")
├── constants/ device sizes, API endpoints, query keys, limits, regex
├── context/ ThemeContext (light / dark), NetworkContext (online / offline)
├── hooks/ useAppDispatch, useAppSelector, useDebounce, useImagePicker, useDocumentPicker
│ └── app/ API hooks: useGetProfile (query), useUpdateProfile (mutation)
├── i18n/ i18next setup and locales (en, ru)
├── redux/ slices (auth, user, settings, loader) and selectors
├── screens/ auth/, app/, tab/ — one folder per screen with index.tsx + styles.ts
├── services/
│ ├── api/ axios instance + interceptors, mock backend, feature APIs (auth, user)
│ ├── errors/ ErrorHandler and status / backend error maps
│ ├── network/ TanStack Query online manager
│ ├── query/ QueryClient
│ └── toast/ Built-in animated toasts (Success / Error / Info / Offline / Context)
├── storage/ store/ MMKV storage, Redux store with redux-persist
├── theme/ palette, light / dark colors, spacing, typography, fonts, layout helpers
├── types/ API, navigation and env types
├── utils/ normalize, date, image compress, pickers, initials, masking
└── validation/ yup rules, schemas and translated messagesImport anything from src with the @/ alias:
import CustomButton from '@/components/shared/CustomButton';
import { useAppTheme } from '@/context/ThemeContext';Included libraries
| Area | Library |
| --- | --- |
| Navigation | @react-navigation/native, native-stack, bottom-tabs, react-native-screens, react-native-safe-area-context |
| State | @reduxjs/toolkit, react-redux, redux-persist, react-native-mmkv |
| Server data | @tanstack/react-query, axios, @react-native-community/netinfo |
| Forms | react-hook-form, yup, @hookform/resolvers |
| UI | @gorhom/bottom-sheet, react-native-reanimated, react-native-gesture-handler, react-native-svg, react-native-linear-gradient, react-native-reanimated-skeleton, @d11/react-native-fast-image |
| Inputs | react-native-element-dropdown, @react-native-community/datetimepicker, rn-international-phone-number, react-native-walkthrough-tooltip |
| Media | react-native-image-picker, @react-native-documents/picker, @bam.tech/react-native-image-resizer |
| Other | i18next, react-i18next, moment, @mhpdev/react-native-haptics |
Shared components
All live in src/components/shared/.
| Component | Use it for |
| --- | --- |
| text/CustomText | Every text. Props: variant (display, header, title, titleMd, titleSm, body, callout, label, subtitle, footnote, caption, micro), weight, textColor, textAlign |
| text/ShimmerText | Text that shows a shimmer while isLoading |
| CustomButton | Buttons with variants, icons, loading, haptics and requiresNetwork (shows an offline toast instead of running) |
| PressableIcon | Tappable icon with haptic feedback, press animation and double-tap protection |
| Icon | Render any SVG from assets/icons by name |
| AppImage | Fast image with loader, placeholder, initials or icon fallback |
| AvatarPicker | Avatar with edit button that opens the camera / gallery sheet |
| DocumentPicker | Ref-based picker for files or camera |
| form-input/FormInput | react-hook-form field with variants: text, dropdown, date, time, country-phone, toggle |
| form-input/FullNameInput | First / last name pair |
| bottom-sheet/BaseBottomSheet, BaseBottomSheetModal | Themed bottom sheets controlled by a ref (open, close) |
| bottom-sheet/sheets/ImagePickerSheet | Ready camera / gallery chooser |
| modal/BaseModal | Centered modal with overlay |
| modal/common/AlertModal | Confirm / alert dialog with icon, title, description and two buttons |
| navigation/StackHeader, HeaderBackButton, HeaderActionButton | Custom stack headers |
| navigation/AnimatedTabIcon, tabIconRender | Animated bottom tab icons |
| offline/OfflineQueryBoundary | Chooses between content, loading UI and offline UI (see below) |
| offline/OfflineState | Full offline screen with animated broken-wifi icon and retry |
| skelton/BaseSkelton | Themed skeleton wrapper |
| skelton/ShimmerHolder | Registry of full-screen shimmers (ShimmerHolder.Home) |
| GlobalLoader | Full-screen loader driven by Redux (showLoader / hideLoader) |
Important patterns
1. Screen data: OfflineQueryBoundary + shimmer
Wrap the content of any screen that loads data. It decides what to render:
| Has data? | Network | Renders |
| --- | --- | --- |
| yes | online or offline | your content (cached data still shows offline) |
| no | online | loading (a shimmer) |
| no | offline | OfflineState with retry |
import { OfflineQueryBoundary } from '@/components/shared/offline';
import { ShimmerHolder } from '@/components/shared/skelton/ShimmerHolder';
import { useGetProfile } from '@/hooks/app';
const Home = () => {
const { data } = useGetProfile();
return (
<OfflineQueryBoundary hasData={!!data} loading={<ShimmerHolder.Home />}>
{/* screen content */}
</OfflineQueryBoundary>
);
};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.
Add a shimmer for a new screen (same three steps every time):
skelton/layouts/<screen>.layout.ts— return the blocks (width, height, borderRadius, margins) that match your screen.skelton/holders/<Screen>Shimmer.tsx— renderBaseSkeltonwith that layout; export it fromholders/index.ts.- Register it in
skelton/ShimmerHolder.tsand use<ShimmerHolder.<Screen> />.
2. Blocking actions: global loader
Use the global loader when the user must wait for an action (submit, upload, delete). For first-time screen data, use a shimmer instead.
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { hideLoader, showLoader } from '@/redux/slice/loader.slice';
const dispatch = useAppDispatch();
const onSave = async () => {
dispatch(showLoader());
try {
await updateProfile(values);
} finally {
dispatch(hideLoader());
}
};See src/screens/tab/profile/index.tsx for a running example.
3. API calls and the mock backend
services/api/axios— oneapiClientwith base URL, timeout, auth token and error interceptors.constants/endpoint.ts— all endpoint paths.services/api/<feature>/— plain async functions per feature (getProfile,updateProfile,login, ...). They unwrap the standard response{ result, message, payload }.
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:
[`get ${ENDPOINTS.app.orders}`]: () => ({
message: 'Orders loaded',
payload: [{ id: 1, title: 'First order' }],
}),When your backend is ready, set API_BASE_URL in .env and restart Metro with yarn start --reset-cache. No screen code changes.
4. Query and mutation hooks
Screens never call axios directly. They use hooks from src/hooks/app/:
useGetProfile()—useQuerywith a key fromconstants/queryKey.ts, caches the profile and mirrors it into Redux.useUpdateProfile()—useMutationthat uploads (compressing images), shows a success toast and invalidates the profile query so every screen refreshes.
Copy these two files as the template for every new feature.
5. Toasts and errors
import { SuccessToast, ErrorToast, InfoToast, ContextToast } from '@/services/toast';
import { ErrorHandler } from '@/services/errors';
SuccessToast({ message: 'Saved' });
ErrorToast({ message: 'Upload failed', action: { label: 'Retry', onPress: retry } });
ContextToast({ icon: 'folder', message: 'File saved to Downloads' });
ErrorHandler(error); // maps HTTP / backend errors to a readable toastToasts are built in (no toast library). ToastHost in App.tsx renders one toast at a time: it opens from a dot, shows a countdown ring, pauses while held, and closes on tap or swipe up. Pass icon, thumbnailUri, action or duration to customise it.
Going offline shows an OfflineToast automatically (NetworkContext). Read the network state anywhere with useNetwork() (isOffline, refresh).
6. Theme, fonts and icons
const { color, mode, setMode } = useAppTheme(); // mode: 'light' | 'dark' | 'system'
<View style={{ backgroundColor: color.background_primary }} />- Colors:
theme/light.tsandtheme/dark.ts(same token names), built fromtheme/palette.ts. - Spacing and layout helpers:
theme/spacing.ts,theme/layout.ts. - Fonts: InterTight, used through
CustomTextweight. - Responsive sizes:
normalize()fromutils/normalize. - Icons: drop an
.svgintosrc/assets/icons, runyarn generate:icons, then<Icon name="my-icon" size={20} color={color.primary} />.
7. Translations
Add keys to src/i18n/locales/en.json (and other locales), then:
const { t } = useTranslation();
<CustomText>{t('profile.editProfile')}</CustomText>Keys are type-checked.
8. Forms
const { control, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(loginSchema),
});
<FormInput control={control} name="email" label="Email" variant="text" error={errors.email?.message} />Schemas live in src/validation/schema/, shared rules and translated messages in src/validation/. See the Login and Sign up screens.
Environment variables
.env.example is copied into the project. Create .env next to it:
# leave unset to use the mock backend
API_BASE_URL=https://api.yourserver.comValues are inlined at build time (process.env.API_BASE_URL). Restart Metro with --reset-cache after changing them. .env is git-ignored.
Useful scripts
| Command | What it does |
| --- | --- |
| yarn android / yarn ios | Run the app |
| yarn start --reset-cache | Start Metro with a clean cache (after .env or babel changes) |
| yarn generate:icons | Rebuild src/assets/icons/index.ts from the SVG files |
| npx react-native-asset | Re-link fonts after adding new ones to src/assets/fonts |
Troubleshooting
| Problem | Fix |
| --- | --- |
| 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 |
| Node version error during init or install | Upgrade Node to 22.13 or newer (node -v) |
| pod install failed at the end | cd ios && bundle install && bundle exec pod install |
| 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 |
| @/... import or .env value not found | Restart Metro with a clean cache: yarn start --reset-cache |
| Fonts missing after adding new ones | npx react-native-asset, then rebuild the app |
| Android build cannot download Gradle | Check your network / proxy and run cd android && ./gradlew clean, then build again |
Contributing
Bug reports and pull requests are welcome on GitHub. See CONTRIBUTING.md for the project layout and how to test changes locally.
Support
If this starter kit saves you time, you can support its development:
