Manual setup
Everything the setup skill does, in the order it does it.
You can hand over at any point. Open the project in a coding agent and say “set up the boilerplate”, and it picks up from wherever you stopped.
Before you start
Install what you need from Tooling. At minimum: Node.js 22+, yarn, git and the Supabase CLI. You can leave Xcode and Android Studio until your first device build.
You also need accounts with Supabase and Expo .
Scaffold the project
npx nativeexpress@latest create-app my-app
cd my-app
yarn installThe scaffolder prompts for app name, slug, bundle identifier, scheme and, optionally,
your Supabase URL and anon key. It writes config.js and .env.local, and mirrors the
agent skills for non-Claude agents.
The clone needs the GitHub access you got at purchase. If it fails with a 404 or a
permission error, check that you are signed in as the account you bought with, and add
--ssh if your GitHub access is SSH-only.
Create your Supabase project
Create a project in the Supabase dashboard , then copy
its project URL and anon key into .env.local:
EXPO_PUBLIC_SUPABASE_URL="<your-supabase-url>"
EXPO_PUBLIC_SUPABASE_ANON_KEY="<your-supabase-anon-key>"Email and password sign-in assumes Verify email is turned off in your Supabase project’s auth settings. Leave it on and new accounts cannot sign in until they confirm, which the boilerplate’s screens do not handle.
Apply the database schema
The project includes migrations and a seed file. Link your project and reset the remote database to apply both:
npx supabase login
npx supabase link
npx supabase db reset --linkedThis creates the threads, messages, generations and scans tables, their
row-level security policies, and the count_monthly_usage function behind the
free-tier quota.
Running Supabase locally instead? Point .env.local at your local instance first, then
run supabase db reset without --linked.
Create the storage buckets
The app needs two buckets: avatars (public) and chat-media (private, owner-scoped).
Create them in your project’s storage section, then set the avatar bucket name in
.env.local:
EXPO_PUBLIC_SUPABASE_BUCKET_NAME="avatars"Avatar uploads and every AI image feature fail without these. chat-media must stay
private, because its policies scope each user to objects under their own user id
prefix.
Set edge function secrets and deploy
At minimum you need OPENROUTER_API_KEY. Follow
Edge Functions → Secrets, then
deploy the functions.
A missing OPENROUTER_API_KEY produces a chat that streams nothing, with no error on
screen. Verify with yarn skills:doctor rather than by testing in the app.
Create an EAS project
eas login
eas initThen add the project id to config.js:
const config = {
general: {
appName: 'My App',
slug: 'my-app',
easProjectId: '<your-eas-project-id>',
owner: '<your-expo-username>', // optional
},
};Set your app identity
If you did not pass these to the scaffolder, set them in config.js now. They must
match the identifiers you register with Apple and Google.
const config = {
general: {
appName: 'My App',
slug: 'my-app',
iosBundleIdentifier: 'com.company.myapp',
androidPackageName: 'com.company.myapp',
},
};Register the bundle identifier as an App ID in your Apple Developer account before your first iOS build. Android package names are claimed on first upload to Play Console.
Run the app
NativeExpress needs a development build, because it uses native modules and custom
config plugins that Expo Go does not include. Build one in the cloud or
locally, install it once, and yarn start connects to it from then on.
Cloud build (no Xcode)
EAS builds it, so you need no local toolchain:
eas build --profile developmentInstall the result on your device, then:
yarn startFor an iOS simulator build instead, use --profile development-simulator.
Optional integrations
None of these are needed to run the app:
- Payments: RevenueCat, and optionally Superwall for paywalls
- Push notifications: OneSignal
- Analytics: PostHog
- Monitoring: Sentry
- Google and Apple sign-in
Run yarn skills:doctor at any point to see which are on, partial or off.