@jalaluddinkhan/react-native-template
v1.0.2
Published
A React Native CLI template with auth flow + bottom-tab navigation, Redux Toolkit + RTK Query, a 16-component accessible UI library, theming, centralized i18n, and a two-tier (Keychain + MMKV) storage strategy already wired up.
Maintainers
Readme
@jalaluddinkhan/react-native-template
A React Native CLI template with the plumbing already wired up: navigation (auth flow + bottom
tabs), Redux Toolkit + RTK Query, a 16-component accessible UI library, theming, centralized
i18n, and a two-tier (Keychain + MMKV) storage strategy — see template/CLAUDE.md
for the full set of project conventions that ship with every generated app.
Use it
npx @react-native-community/cli@latest init MyApp --template @jalaluddinkhan/react-native-template
cd MyApp
yarn ios # or yarn androidThe CLI copies template/ into MyApp and replaces every occurrence of
RNTemplate/rntemplate (iOS target, Android package com.rntemplate, package.json name,
etc.) with your new project's name automatically — no manual renaming needed.
What's included
Everything below is already written, tested, and wired together in template/src/ — not just
a list of installed dependencies. It ships with 168 passing tests across 47 suites (co-located
next to what they test) so you can verify all of it yourself with yarn test the moment you
generate a project.
Auth flow
Login, Sign Up, Forgot Password, and Reset Password screens (src/screens/Auth/) — dummy/
simulated (no backend), but wired through a real useAuth hook (src/hooks/useAuth.ts), a
Redux slice (src/store/slices/authSlice.ts), and a persisted session that survives an app
restart. Every field uses Formik + Yup validation with translated error messages. Replace
fakeLogin in useAuth.ts with a real API call (an RTK Query mutation) when you have a
backend — the token/session-restore plumbing around it doesn't change. Logging out asks for
confirmation via the reusable ConfirmDialog component rather than logging out immediately.
Navigation (src/navigation/)
RootNavigator switches between the unauthenticated stack and the authenticated app based on
useAuth's status, built with React Navigation (native-stack + bottom-tabs), fully typed route
params:
RootNavigator
├── AuthNavigator (stack) — shown when logged out
│ ├── Login
│ ├── SignUp
│ ├── ForgotPassword
│ └── ResetPassword
└── MainNavigator (stack) — shown when logged in
├── MainTabs (bottom tabs)
│ ├── Home
│ ├── Profile
│ └── Settings
└── Showcase — pushed from Settings → "Component Showcase"Post-login app
Home, Profile, and Settings screens on a bottom tab bar. Settings has working theme and
language switchers, and links to the original component/hook/util showcase (Component
Showcase) so none of the demo library is lost as you build your real screens on top of it.
Two-tier storage
Deliberately split by sensitivity, not just by preference:
- Auth tokens →
react-native-keychain— written straight to the OS-level secure store (iOS Keychain / Android Keystore), which encrypts at rest and is sandboxed per app. Never persisted via Redux/redux-persist or MMKV. - Everything else →
react-native-mmkv— theme mode, language, notes, and the non-sensitive cached user profile. Chosen over AsyncStorage for speed (synchronous, memory-mapped) since none of this data is sensitive; also wired as theredux-persiststorage engine. - Why not MMKV for tokens too? MMKV doesn't encrypt by default, and even its optional encryption key has to live somewhere — the Keychain/Keystore is the actual OS-provided secure element and is the standard place for credentials in a mobile app.
Centralized i18n
Every user-facing string, including validation messages, lives in
src/utils/locales/en.json / hi.json and is resolved via useTranslation()/t() — no
screen or component hardcodes its own copy, so adding a language means editing those two files,
not hunting through the codebase. English and Hindi ship out of the box, with a working
language switcher on the Settings screen.
State & data (Redux Toolkit + RTK Query)
src/store/index.ts— root store, withredux-persist(MMKV-backed) persisting only the slices that should survive a restart (currentlynotes;authis deliberately excluded — see Two-tier storage above).src/store/slices/—authSlice(session status/user) andnotesSlice(a small persisted CRUD slice demonstrating the pattern for your own feature slices).src/services/api/— an RTK QuerycreateApibase (apiSlice.ts) with one injected endpoint (postsApi.ts) as a worked example of theinjectEndpointspattern for adding your own domain APIs.src/services/socket/SocketClient.ts— a typed WebSocket client (connect/reconnect/send/ parse-incoming) with a matchinguseSockethook, for real-time features outside of request/response HTTP.
UI component library (src/components/ui/)
16 components, each with its own test file, built on shared theme tokens (no hardcoded colors or spacing, dark-mode aware, meeting the 44×44pt minimum touch target for anything tappable):
| Component | What it's for |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Button | Primary/secondary/outline/ghost/destructive variants, loading state, left/right icon slots |
| Text | Typed wrapper mapping the typography scale + theme colors, respects OS font-scaling settings |
| Input | Label, error/helper text, left/right icon slots, and a built-in show/hide toggle for secureTextEntry (password) fields |
| Dropdown | Single-select popover list with up/down placement that adapts to available screen space |
| Card | Themed container, optionally pressable |
| Avatar | Image with graceful fallback to initials |
| Badge | Small status/count pill in semantic colors (success/warning/error/info) |
| Divider | Horizontal/vertical rule using theme spacing |
| EmptyState | Icon/illustration + title + description + optional action, for empty lists/search results |
| LoadingSpinner | Themed ActivityIndicator, with a full-screen overlay variant (used e.g. while a persisted session rehydrates) |
| Modal | Accessible modal wrapper — backdrop-press-to-dismiss, safe-area aware |
| ConfirmDialog | "Are you sure?" wrapper around Modal — confirm/cancel buttons, optional destructive (red) styling; this is what powers Settings' logout confirmation |
| BottomSheet | Draggable bottom sheet built on Reanimated + Gesture Handler |
| ListItem | Standard row layout (leading/trailing slots, title/subtitle) for settings rows, list rows, etc. |
| SafeScreen | SafeAreaView + consistent horizontal padding, so every screen doesn't reimplement this |
| ErrorBoundary | Class component that catches render errors in its subtree and shows a themed "Something went wrong" fallback with a retry button, instead of a blank/crashed screen |
Hooks (src/hooks/)
Beyond useAuth, the typed Redux hooks useAppDispatch/useAppSelector, and the theme/i18n
hooks useAppTheme/useThemeMode/useAppLanguage, there's:
useDebounce— debounce any fast-changing value (e.g. search input)useKeyboard— keyboard visibility + height, for keyboard-avoiding layoutsuseAppState— current foreground/backgroundAppStateuseMountedRef— guard against setting state after unmount in async callbacksuseSocket— the hook half ofSocketClientabove
Utilities (src/utils/)
formatters.ts—formatDate,formatDateTime,formatCurrency,formatNumber,formatCompactNumber,formatRelativeTimevalidators.ts—isValidEmail,isValidPhone,isRequired,hasMinLength,hasMaxLength,isInRangeerrorHandler.ts—normalizeError, turning thrown values of unknown shape into a consistent{ message, code? }for UI displaylogger.ts— the one placeconsole.*is allowed (everywhere else is ESLint-blocked viano-console); swap in a real crash reporter (Sentry, Crashlytics) viasetCrashReporterwithout touching any call sitesi18n.ts— the i18next setup wiringlocales/en.jsonandlocales/hi.json
Theme (src/theme/)
Design tokens — colors.ts (full light/dark palettes), spacing.ts (spacing/radius/shadow/
touch-target scale), typography.ts (font scale), breakpoints.ts (tablet breakpoint) — plus
ThemeModeProvider/useThemeMode for the light/dark/system toggle, persisted via MMKV so the
choice survives a restart.
Feature showcase screens
Beyond the post-login app, four demo screens (reachable via Settings → Component Showcase)
exercise everything above end-to-end: ComponentShowcaseScreen (every UI component + RTK
Query), FormDemoScreen (a realistic Formik + Yup form with a date picker and dropdown),
DeviceFeaturesScreen (network status, image/document pickers), and MediaShowcaseScreen
(FlashList, Reanimated + Gesture Handler, inline SVG icons, WebView). NotesScreen demonstrates
the persisted-slice pattern end-to-end — add a note, close the app, it's still there.
Testing
Every component, hook, slice, and screen above has a co-located Jest + React Native Testing
Library test file — see template/CLAUDE.md's Testing Rules for the
conventions they follow (real store over mocked hooks, network mocking over mocked generated
hooks, etc.).
See template/README.md for the getting-started guide (Metro, running
on Android/iOS, project structure) that ships inside every generated app.
Repository layout
template.config.js # tells the RN CLI where the template lives and what to rename
template/ # everything that gets copied into a new project
CLAUDE.md # project conventions, shipped into every generated app
src/ # the actual app code
ios/, android/ # native projects (already named "RNTemplate" — the placeholder itself)
...template/ is a normal, runnable React Native project — clone this repo, cd template,
yarn install, and yarn ios/yarn android to develop or test the template itself before
publishing changes.
Publishing updates
After changing anything under template/, verify it still works end-to-end before publishing:
cd template && yarn lint && yarn typecheck && yarn test && cd ..
npx @react-native-community/cli@latest init TestApp --template file:$(pwd)Then bump the version in this root package.json and run npm publish (requires being logged
in as a maintainer of the @jalaluddinkhan npm scope: npm login).
Alternative: bootstrap script
bootstrap-rn-app.sh is a legacy alternative to the --template flow
above: it scaffolds a fresh RN project, then installs this template's dependencies at their
current latest on npm (rather than whatever's pinned in template/package.json) before
copying the source over. Prefer --template @jalaluddinkhan/react-native-template for normal
use; reach for the script only when you specifically want dependencies re-resolved to latest.
./bootstrap-rn-app.sh MyNewApp