{"id":28181056,"url":"https://github.com/strvcom/template-react-native-expo","last_synced_at":"2025-05-16T03:12:06.529Z","repository":{"id":288249236,"uuid":"491802947","full_name":"strvcom/template-react-native-expo","owner":"strvcom","description":null,"archived":false,"fork":false,"pushed_at":"2025-05-08T16:15:04.000Z","size":1423,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-05-08T17:28:45.249Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/strvcom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-05-13T07:38:44.000Z","updated_at":"2025-05-07T10:57:17.000Z","dependencies_parsed_at":"2025-04-16T13:27:31.096Z","dependency_job_id":"14fea270-165f-4827-83d4-336ee18ac873","html_url":"https://github.com/strvcom/template-react-native-expo","commit_stats":null,"previous_names":["strvcom/template-react-native-expo"],"tags_count":5,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strvcom%2Ftemplate-react-native-expo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strvcom%2Ftemplate-react-native-expo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strvcom%2Ftemplate-react-native-expo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strvcom%2Ftemplate-react-native-expo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/strvcom","download_url":"https://codeload.github.com/strvcom/template-react-native-expo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254459087,"owners_count":22074606,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-16T03:12:05.389Z","updated_at":"2025-05-16T03:12:06.504Z","avatar_url":"https://github.com/strvcom.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Native Expo Template\n\n**Table of Contents**\n\n- [React Native Expo Template](#react-native-expo-template)\n  - [Overview](#overview)\n  - [Quick Start](#quick-start)\n  - [Important Defaults - Configuration](#important-defaults---configuration)\n    - [Expo Development Build](#expo-development-build)\n    - [EAS Build Setup](#eas-build-setup)\n    - [App Environments Setup](#app-environments-setup)\n    - [Babel Plugins](#babel-plugins)\n    - [Debugging - Expo Dev Plugins](#debugging---expo-dev-plugins)\n    - [Linting Tools](#linting-tools)\n  - [Important Defaults - APP](#important-defaults---app)\n    - [Global State and User Persistence](#global-state-and-user-persistence)\n    - [Over-the-Air Updates](#over-the-air-updates)\n    - [Forced Update aka Minimum Version Check](#forced-update-aka-minimum-version-check)\n    - [Offline Check](#offline-check)\n    - [Maximum Font Scaling](#maximum-font-scaling)\n    - [Size Scaling](#size-scaling)\n  - [Other Recommended Solutions](#other-recommended-solutions)\n  - [Adding new `ENV` variables:](#adding-new-env-variables)\n\n## Overview\n\nThis template is a starting point for building a React Native app using Expo in STRV.\n\nIt provides a foundation for every stage of the development process:\n\n🔧 For the project start and initial development:\n\n- Expo Development build setup\n- domain driven folder structure\n- Storage service using MMKV\n- Font and size scaling utilities\n\n📏 For keeping the code quality high and enforcing code standards:\n\n- basic unit test setup\n- Linter and formatter setup\n- pre push and pre commit hooks\n\n🚀 For shipping and production maintenance:\n\n- Github Actions for CI/CD\n- App config ready for different environments\n- Utilities for version check and forced updates in production\n\n\u003e ⚠️ We don't include any more opinionated solutions such as:\n\u003e\n\u003e - Styling and theming solution\n\u003e - State management library\n\u003e - Image loading library\n\u003e - E2E testing setup\n\u003e   You need to decide what is best for your project and add it to the project. But we provide some recommendations in the [Other Recommended Solutions](#other-recommended-solutions) section.\n\n## Quick Start\n\n1. Clone the repository\n2. Run `pnpm install` to install dependencies\n3. copy `.env.example` to `.env` using `cp .env.example .env`\n4. run `pnpm ios/android` to start the development server and run the app on your device\n\nTo use the full CI/CD pipeline you also need to:\n\n1. [Setup EAS and EAS credentials](docs/eas-setup.md)\n2. [Setup Github environment](docs/github-setup.md)\n3. [Setup Slack app for notifications (optional)](docs/slack-setup.md)\n4. [Jira Setup (optional)](docs/jira-setup.md)\n\n## Important Defaults - Configuration\n\n### Expo Development Build\n\n- The main benefit is better maintainability as most of the native setup is done through `app.config.ts` and community/custom config plugins. Updating Expo SDK mostly assures compatibility with majority of dependencies used, which is a common source of problem when upgrading React Native separately. You can use expo doctor to check for any issues with the setup and update the project accordingly.\n\n### EAS Build Setup\n\n- EAS helps with building and app submission. It can create and store all important credentials so that we don't have to distribute them among everyone.\n- Default build profiles in `eas.json`:\n  - `dev` - this profile will build an `expo-dev-client`, meaning that after installing the app, one can change non-native code and see changes reflected in the app\n  - `staging` - should be distributed for testing, does not have a dev client, meaning it cannot be manipulated. It builds `com.xxx.xxx.staging` application which can be distributed through a link or a QR code.\n\n\u003e For iOS, we need to add devices for EAS testing (development, staging) via `eas device:create` (creates sharable registration link) and confirm the device has been added. It is good to write down your unique device ID to understand what is your device.\n\n- `production` - non-distributable build that should be submitted to Play Store and App Store. Can be tested through Play Store internal testing track or Testflight. It builds the official `com.xxx.xxx` package later released to production.\n\n### App Environments Setup\n\n- Environment variables are managed by [Expo](https://docs.expo.dev/guides/environment-variables/), use `EXPO_PUBLIC` prefix to make them accessible in the app\n- `app.config.ts` determines based on the environment a relevant **icon, app name and appIdentifier** to distinguish individual apps and allow installing side by side\n- `eas.json` can set `APP_ENV` variable for each build profile to define environment\n\n### Babel Plugins\n\n- `babel-plugin-transform-remove-console` to remove console logs in production\n\n### Debugging - Expo Dev Plugins\n\nDev plugins included in the template:\n\n- [React Navigation](https://docs.expo.dev/debugging/devtools-plugins/#react-navigation) - to see navigation state, history, and params passed to screens\n- [MMKV](https://github.com/expo/dev-plugins/blob/main/apps/example/src/app/react-native-mmkv/index.tsx) - to see MMKV store\n\nNot included but useful:\n\n- [React Query](https://github.com/bgaleotti/react-query-native-devtools)\n\n### Linting Tools\n\n- We are using @strv/eslint-config-react-native config which is an extension of Expo Universe config with couple of extra rule changes we have found useful.\n- We are using mostly standard prettier config.\n- Husky is used to run linting and formatting before committing and pushing code.\n- Lint Staged is used to run linting and formatting before committing.\n\n## Important Defaults - APP\n\n### Global State and User Persistence\n\nUser persistence is setup through `MMKV` which is a **synchronized** and **faster** alternative to `AsyncStorage`.\n\n### Over-the-Air Updates\n\n`expo-updates` is used to deliver and check for new updates. `useOTAUpdates()` checks if the update is available on mount and whenever the app goes from background to foreground via `useOnForeground()` hook. If an update is available, we should show to the user screen/modal/alert that would suggest them to update (reload the app).\n\n\u003e ⚠️ The OTA updates are used for patches to javascript layer only which is convenient for small bug fixes and UI changes.\n\nTo deliver the update through `eas update` we need to target the right `channel` from `eas.json` and manage `runtimeVersion` in `app.config.ts` for **native layer compatibility** otherwise we risk updating incompatible environment resulting in app crashes.\n\n\u003e ⚠️ runtimeVersion should be changed whenever we change native layer, meaning a new native third party dependency is installed in package.json or we do native config changes in app.config.ts.\n\n\u003e ❓ A versioning strategy could be to bump minor version for every native change and bump patch version for every javascript change. Then `runtimeVersion` would change from `0.1.0` to `0.2.0`, while `version` could change as follows: `0.1.0` \u003e `0.1.x` \u003e `0.2.0`. This means `runtimeVersion` `0.1.0` is compatible with all `versions` `0.1.x`.\n\n\u003e ⚠️ if we run `eas update` locally, current `.env` file is used, so be careful not to publish to production development variables. Better to do it through a github action and setup environment variables as Github Secrets.\n\n### Forced Update aka Minimum Version Check\n\n**Before going to production** we should have a forced update functionality in place for cases such as when we introduce a major bug or our backend API is not backward compatible. `useStoreUpdate()` hook compares `minimumSupportedVersion` or `recommendedVersion` with `installedAppVersion` from `app.config.ts`. The minimum version should come either from some backend API or third party service such as `Firebase Remote Config` (good experience). If the app is outdated we should show to the user screen/modal/alert that would suggest them to update (redirect to store listing).\nHow such a modal could look like is included in the template.\n\n### Offline Check\n\n`useNetInfo()` provides information about current user's network connectivity. In case of `isConnected` returns false, we can provide a helpful hint (`\u003cOfflineMessage/\u003e` or full screen) to the user that they are disconnected from the internet so that they don't expect full app functionality. Also provide button to either `fetch()` the latest connection info or `reload` the app should they get stuck.\n\n\u003e True is the listener is not fully [reliable](https://github.com/react-native-netinfo/react-native-netinfo/issues/481) and I see on my Pixel that I am not connected when changing from background to foreground even though I am\n\n### Maximum Font Scaling\n\nReact Native allows as default to scale the font significantly which will break the UI of the app. You should expect users to have font scaling up if your target group includes older generation. To allow some level of accessibility but prevent users from breaking the app UI, default scaling of maximum +25% is applied. You can increase it but be sure to control important parts of the application individually to keep UX in place.\n\n### Size Scaling\n\nTo replicate Figma design consistently on majority of mobile screen sizes, we should apply size scaling to UI elements relative to actual device window width/height. This technique is not perfect and implements a subjective scaling factor, but prevents well having too small elements on larger screens. Inspiration: [article + library](https://github.com/nirsky/react-native-size-matters/blob/master/examples/BlogPost/README.md)\n\n## Other Recommended Solutions\n\n- **Styling**\n\n  - [Restyle](https://github.com/Shopify/restyle), [Unistyles](https://www.unistyl.es/), [Nativewind](https://www.nativewind.dev/)\n\n    \u003e Restyle follows a defined theme with strict type safety resulting in consistent and quickly built UI. It is very helpful when a designer defines majority of text variants which can be plugged into the theme and reused super easily. It has also responsive utilities that can make potential transition to a tablet app easier. Unistyles takes a similar approach but is newer and doesn't require special components. Nativewind is a newer library that is based on Tailwind CSS.\n\n- **Notifications**\n\n  - [React Native Firebase Cloud Messaging](https://rnfirebase.io/messaging/usage) + [Notifee](https://github.com/invertase/notifee)\n\n    \u003e Both managed by Invertase with latest notification features. Notifee is needed to change Foreground notifications to local ones. Expo-notifications, alternative to both, is also an option but only with native tokens, because using ExpoPushTokens is a strong lock-in, not easily reverted.\n\n- **Forms**\n\n  - [React Hook Form](https://github.com/react-hook-form/react-hook-form) + [Zod](https://github.com/colinhacks/zod)\n\n    \u003e RHF offers many more utilities (and less bugs) than Formik, e.g. to name one, with `setFocus(fieldName)` one does not have to setup own refs for inputs. Zod for validation is typescript first and type inference is very reliable and useful.\n\n- **Bottom Sheets and Modals**\n\n  - [React Native Bottom Sheet](https://github.com/gorhom/react-native-bottom-sheet)\n\n    \u003e Reliable all-in-one solution with good Keyboard Handling options\n\n- **Swiping content**\n\n  - [React Native Pager View](https://github.com/callstack/react-native-pager-view)\n\n    \u003e From Callstack, actively maintained and already supporting the new Architecture, uses native components.\n\n- **In-App Purchases**\n\n  - [React Native Purchases](https://github.com/RevenueCat/react-native-purchases)\n\n    \u003e Ready-to-go solution from RevenueCat, used on Arnold and Showdown projects.\n\n## Adding new `ENV` variables:\n\nWe highly recommend using EAS to manage environments.\n\nWe map our environment names to these Expo environment names.\n\n```\n\"dev\": \"development\",\n\"staging\": \"preview\",\n\"production\": \"production\"\n```\n\nInside Github Actions we use an action to map the environments automatically.\nTo add new environment variable:\n\n- Either add it through the Expo dashboard\n- Or through eas cli:\n\n```\neas env:create \u003cEXPO_ENVIRONMENT\u003e --name \u003cname\u003e --value \u003cvalue\u003e\n```\n\n\u003e ⚠️ All variables must use the `EXPO_PUBLIC` prefix! e.g., `EXPO_PUBLIC_API_URL`\n\nYou can also pull variables from EAS to your local `.env` file:\n\n```\neas env:pull \u003cEXPO_ENVIRONMENT\u003e --path \u003cpath-to-env-file\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrvcom%2Ftemplate-react-native-expo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstrvcom%2Ftemplate-react-native-expo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrvcom%2Ftemplate-react-native-expo/lists"}