Yet another starter template for Expo (React Native) projects!
- Install
pnpm
if you haven't.
$ npm install -g pnpm
- Create your project using the template.
# will always clone the latest version of the template
$ pnpm create avid-app@latest <your_project_name>
.
├── assets/ # Static asset files (images, icons, etc.)
├── src/
│ ├── app/ # Navigators and screens
│ ├── services/ # Services (api, storage, etc.)
│ ├── theme/ # App theme provider
│ ├── ui/ # UI primitives (button, text, etc.)
│ └── env.ts # Read up on "Env management" in the readme
│
├── .env.development # Env file for local dev
├── index.js # Main entrypoint to the app
├── app.config.ts # Expo config file
├── build-env.ts # Read up on "Env management" in the readme
└── tailwind.config.js # Tailwind config used by `twrnc` (read up on "Styling")
These packages are included out of the box:
- ESLint, Prettier, TypeScript
react-navigation v6
(this template doesn't useexpo-router
)react-native-svg
react-query
(basic setup)husky
(runs a check onpre-push
by default)tabler-icons
andtwrnc
(read more on styling below)zod
for validating.env
schema (read more on env management below)
This template uses twrnc
(a performant TailwindCSS-based library) for styling and supports dark mode by default
(responding to the device's theme preference).
Instead of creating stylesheets, you style components inline like this:
import { Text, View } from "react-native";
import { tw } from "@/theme";
function Card({ disabled }: { disabled: boolean }) {
return (
<View
style={tw.style("p-6 bg-background dark:bg-background-dark", {
"opacity-50": disabled,
})}
>
<Text
style={tw`font-semibold text-primary-foreground dark:text-primary-foreground-dark`}
>
Hello
</Text>
</View>
);
}
Tip
This template also includes the Prettier plugin to sort Tailwind classes based on the recommended order.
This has several benefits:
- not having to think of unique names for each and every component style (speeding up UI prototyping)
- if you already use Tailwind in your web project, you can reuse your existing
tailwind.config.js
(check out this template'stw
config file) - supporting dark mode is now as simple as using the
dark:
modifier (e.g.bg-white dark:bg-black
) in your component styles - adapting different styles for Android and iOS is as simple as using the
android:
andios:
modifiers respectively
Several potential downsides:
- you now have to be familiar with Tailwind classes if you weren't previously
- not every Tailwind class is supported — check out the library's docs for more info
- styles can be quite long and ugly (spanning across lines)
Env variables are validated with zod
at build time to ensure consistent/correct
values. Unlike Expo's default mechanism
to set and read env variables (which this template doesn't use), the variables
you define don't need to be preprended with EXPO_PUBLIC
(although you should
still assume that they're accessible by your users!).
- Add the key-value pair to any/all of these env files:
.env.development
.env.preview
.env.production
# .env.development
YOUR_BASE_URL="https://yoursite.com"
- Add the key name to the
zod
schema inbuild-env.ts
.
// build-env.ts
const ENV_SCHEMA = z.object({
YOUR_BASE_URL: z.string(),
// your other keys...
});
This file is where you set the schema of your env variables. It has detailed comments explaining what it does—feel free to go through them. During builds, this file loads env variables and parses them, throwing a build error if the values don't match the defined schema.
- Import
Env
from@/env
in your screens/components to consume the env variable.
import { Env } from "@/env";
async function getUsers(): Promise<User> {
// `YOUR_BASE_URL` will be typechecked if you define it as part of the schema
// in `build-env.ts`.
const res = await fetch(`${Env.YOUR_BASE_URL}/api/users`);
return res.json();
}
Remove the env variable from both the env file (e.g. .env.development
) and
the schema in build-env.ts
.
To choose which environment to load variables from, set the APP_ENV
env variable
to either one of: development (default if unspecified) | preview | production
.
pnpm
scripts have been added to help simplify this.
// package.json
{
"scripts": {
// These 3 scripts run the `Debug` configuration.
// If `APP_ENV` isn't specified, it's `development` by default.
"ios": "EXPO_NO_DOTENV=1 expo run:ios",
"ios:preview": "APP_ENV=preview pnpm ios",
"ios:prod": "APP_ENV=production pnpm ios",
// These 3 scripts run the `Release` configuration.
"ios:release": "EXPO_NO_DOTENV=1 expo run:ios --configuration Release",
"ios:preview:release": "APP_ENV=preview pnpm ios:release",
"ios:prod:release": "APP_ENV=production pnpm ios:release",
// Similar scripts for Android have also been added.
},
}
If env variables are successfully parsed according to the defined schema, they
are consumed in app.config.ts
as Expo extra fields. These
fields are then re-exported by src/env.ts
to the rest of the
app (screens/components).
src/env.ts
acts only as the proxy for Expo extra fields to improve the type-safety
of consuming env variables. This file doesn't need to be modified at all (unless
you're changing the entire mechanism of defining and consuming env variables).
Licensed under MIT.