# React Native Template Obytes: the Expo starter that pins pnpm, environments and commit hooks

> Obytes publishes a React Native starter built on Expo, expo-router, TypeScript and TailwindCSS, aimed at teams who build several client apps and want every one of them to start from the same scaffolding. It trades flexibility for a fixed set of choices, including pnpm only and a macOS-only script.

**obytes/react-native-template-obytes** — 📱 A template for your next React Native project: Expo, PNPM, TypeScript, TailwindCSS, Husky, EAS, GitHub Actions, Env Vars, expo-router, react-query, react-hook-form.

- Repository: https://github.com/obytes/react-native-template-obytes
- Website: https://starter.obytes.com
- Stars: 4,356 · Forks: 674
- Language: TypeScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/obytes-react-native-template-obytes

## The starter exists so client apps share one scaffolding

Most React Native projects spend their first two weeks assembling the same pile of decisions: which router, where forms live, how secrets reach a device build, what runs on commit. Obytes built this template to stop paying that cost per client. The stated motivation is switching between projects without relearning the layout, and the reason the code is shaped the way it is: the team wanted business logic to be the part that changes, not the wiring around it.

That framing has consequences you can see in the file list at the repository root. There is an `app.config.ts` rather than a static app config, a TypeScript `env.ts` at the top level, a `.husky/` directory, a `cli/` folder and a `docs/` directory. The template is opinionated about project structure on purpose, and the second-order effect is that adopting it means adopting that structure.

One piece of internal evidence is worth pausing on. The repository description still advertises `react-hook-form`, while the README and the current dependency set name TanStack Form with zod. The form layer was replaced, and the metadata was not. Anyone choosing this template should read the README and `package.json` rather than the one-line summary, because the summary lags behind the code.

## A preinstall hook turns npm and yarn into a hard error

The package manager is not a suggestion here. `package.json` declares `packageManager` as `pnpm@10.12.3` and adds a `preinstall` script that runs `npx only-allow pnpm`. If someone clones the project and reaches for npm, install stops before it writes a lockfile. A `pnpm-lock.yaml` at the root is the only lockfile, so the dependency set the project was built against belongs to pnpm alone.

Two scripts depend on the same toolchain. The `doctor` script shells out to `npx expo-doctor@latest`, and every environment-prefixed script goes through `cross-env` so that `EXPO_PUBLIC_APP_ENV` is set on macOS, Linux and Windows alike. If your CI runner or your onboarding checklist still uses npm or yarn, expect to spend the first day of the adoption rewriting it.

This is the template's clearest opinion, and it is also its cheapest one to accept or reject. Nothing in the runtime code depends on pnpm, but the lockfile, the preinstall guard and the cross-env scripts all assume it.

## app.config.ts and env.ts turn one bundle into three environments

Instead of one config and one build, the template ships development, preview and production as parallel script families. Each family covers a start script, a prebuild script, a run script for android and ios, and an EAS build script per platform, which is twelve scripts before you write any application code. The environment is selected by setting `EXPO_PUBLIC_APP_ENV` through `cross-env`, not by editing a file.

The prebuild variants add a second variable, `STRICT_ENV_VALIDATION=1`, while the plain `start` variants do not. Read together, that says the validation gate applies at native generation time rather than at dev-server start: the moment Expo writes out the native directories is the moment a missing variable should stop the build. A developer running the app locally gets a lenient path, a build machine gets a strict one.

Environment variables reach the app through the top-level `env.ts`, and the `EXPO_PUBLIC_` prefix means Expo inlines those values at build time rather than reading them from the device. The practical consequence for a reviewer is that a value present in your shell is not the same as a value present in a production build, and the strict prebuild scripts are the guardrail against discovering that difference after a build ships.

## Four commands get you from clone to a running Android app

There is no install command in the README excerpt beyond pointing at the repository, so the honest starting point is the lockfile and the scripts already in `package.json`. The preinstall guard runs first and tells you which package manager is allowed:

```bash
npx only-allow pnpm
```

Install with pnpm so that `pnpm-lock.yaml` stays authoritative. Once dependencies are in place, the doctor script is the fastest way to confirm the Expo SDK matches what the template expects:

```bash
npx expo-doctor@latest
```

Start the development server, or point the same script at the preview environment:

```bash
cross-env EXPO_PUBLIC_APP_ENV=preview pnpm run start
```

For a native build rather than the Expo Go path, the `android` and `ios` scripts are what you want:

```bash
pnpm run android
```

Startup is owned by the router. `package.json` sets `main` to `expo-router/entry`, so the router owns startup and your file-based routes under `src/` decide what renders. A blank screen after start is usually a routing problem rather than a bundler problem.

## Husky and lint-staged move the review before the pull request

Two directories and one config file handle what would otherwise be CI feedback. `.husky/` installs the Git hooks, `lint-staged.config.js` runs ESLint and TypeScript checks against staged files, and `commitlint.config.js` governs commit message shape. The point is that a failing lint rule costs the developer a commit message, not a pull request review cycle.

That works only if the hooks actually fire, and hooks are exactly the thing a new machine gets wrong. Clone with Git hooks enabled, install dependencies so the hook installer runs, and confirm the `.husky` directory is executable. A team that copies the files into an existing repository and forgets the hook step keeps the ESLint config and loses the enforcement.

The same logic applies to `.github/`. The template ships more than ten workflows covering builds, tests and distribution, which is a lot of YAML to inherit. Read them before your first upload, because workflows copied from a template frequently carry the original author's branch names, signing setup and tagging scheme.

## The xcode script only runs on a Mac, and nothing replaces it

One script in `package.json` narrows the audience more than the README does: `"xcode": "xed -b ios"`. `xed` is Xcode's command line launcher and exists on macOS alone. On Linux or Windows the script fails immediately, and the template offers no cross-platform substitute in the same file. Building for iOS locally is a Mac-only operation, and this repository encodes that assumption directly rather than leaving it to the platform docs.

The second sharp edge is how much arrives pre-decided. TailwindCSS runs through NativeWind, forms go through TanStack Form and zod, server state goes through React Query and axios, and auth state is kept in Zustand with storage in react-native-mmkv. That is a coherent stack, and each piece is defensible. It is also a stack you now own: replacing any of it means editing the template's source rather than swapping a config value.

The MIT licence covers the source without conditions beyond attribution. The cost sits in the dependency graph, which `pnpm-lock.yaml` pins, and it shifts whenever the template shifts. The last push landed on 2026-06-02, so plan upgrades around the tags rather than around weekly drift.

## Why the template picks Expo over the bare React Native CLI

The README answers the framework question directly rather than leaving it open. Its position is that the choice stopped being real once Continuous Native Generation arrived, and it quotes the React Native core team saying Expo is the only recommended community framework for new projects. That argument rests on tooling, not on runtime: CNG regenerates native project files from `app.config.ts` and the dependency versions, which is why `prebuild` exists as a script at all.

The practical difference from starting from the React Native CLI is the source of truth for native configuration. In a CLI project you own the ios and android directories as checked-in source and edit them directly. Here they are generated output, and the checked-in files are plugins and config. Teams that need to patch native code by hand will feel this, and the template's answer is a custom dev client plus the plugin system rather than a fork of the native project.

The Expo comparison to watch is not another template but Expo's own default TypeScript template. That one is smaller and makes fewer commitments: no Husky, no lint-staged, no typed environment layer, no ten CI workflows. Choose this repository when the plumbing is the expensive part of your work, and choose the default template when you would rather assemble that plumbing yourself.

## Conclusion

Adopt it when you are starting a second Expo app inside an agency or a small product team and would rather inherit typed environment variables, commit hooks and CI wiring than write them. Do not adopt it for a bare React Native CLI project where ios and android directories are checked in as source, for a Windows or Linux workstation, or for a team that installs with npm or yarn. Verify three things in order. First run pnpm run doctor and read what the Expo SDK check reports, since the template pins one SDK generation. Second run a prebuild script with STRICT_ENV_VALIDATION=1 and confirm env.ts rejects a missing EXPO_PUBLIC_APP_ENV value. Third, on a Mac, run pnpm run xcode and confirm xed opens the generated project. If any of those three fail on your machine, the template will not save you time.

## FAQ

### How do I install the React Native Template Obytes starter?

The repository ships a pnpm lockfile and a preinstall guard that runs npx only-allow pnpm, so pnpm is the only supported package manager. npm and yarn installs are rejected on purpose.

### What package manager does react-native-template-obytes require?

package.json pins packageManager to pnpm@10.12.3 and the lockfile at the repository root is pnpm-lock.yaml. Installing with npm or yarn fails at the preinstall step.

### How do I run a preview build in react-native-template-obytes?

Set EXPO_PUBLIC_APP_ENV=preview through the cross-env wrappers, for example with the start:preview script. The prebuild variants also set STRICT_ENV_VALIDATION=1 so missing variables stop native generation.

## Sources

- [License: MIT](https://github.com/obytes/react-native-template-obytes/blob/master/LICENSE)
- [obytes/react-native-template-obytes on GitHub](https://github.com/obytes/react-native-template-obytes)
- [Project website](https://starter.obytes.com)
- [README](https://github.com/obytes/react-native-template-obytes/blob/master/README.md)
- [Releases](https://github.com/obytes/react-native-template-obytes/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/obytes-react-native-template-obytes
