Installing the Expo app | Expo Supabase SaaS Kit
Start a new project from the Expo kit, or add the Expo app to a Makerkit Next.js + Supabase project you already have, using the CLI plugin.
There are two ways to get the Expo app. Which one you want depends on whether you are starting a project or adding mobile to one that already exists.
| Start from this kit | Add as a plugin | |
|---|---|---|
| You have | Nothing yet | A Makerkit Next.js + Supabase project |
| You get | apps/web and apps/native together, already wired | The same apps/native, copied into your repo and wired to it |
| How | git clone | npx @makerkit/cli@latest plugins add expo |
| Your existing work | N/A | Untouched — the plugin only adds files and edits config |
| Kit variants | This kit only | next-supabase only — see below |
Both end at the same place: an Expo app in apps/native, sharing a Supabase project and the @kit/* packages with the web app. If you are starting fresh, take the first path — it is a clone, with nothing to go wrong.
Option 1 — start from the Expo kit
This repository is a complete Makerkit kit. It contains the full Next.js web app and the Expo app, already wired together. Cloning it gives you both.
The CLI's new command lists the seven core kit variants (Next.js, React Router and TanStack Start, each with Supabase, Drizzle or Prisma). The Expo kit is a variant of the Next.js + Supabase kit rather than a separate entry, so clone it directly.
git clone https://github.com/makerkit/expo-supabase-turbo-saas-kit.git my-appcd my-apppnpm installPoint origin at your own repository:
git remote set-url origin https://github.com/<you>/<your-repo>.gitgit push -u origin mainThen start the web stack and the app. docs/native/running-locally.mdoc covers this in full — including which URL each device class needs — but the short version is:
pnpm run supabase:web:start # local Supabasepnpm run dev # the Next.js app on :3000pnpm run ios:native # or: pnpm run android:nativeThe committed apps/native/.env.development already points at the local Supabase stack and the local web app, so there is nothing to configure before the first run.
Option 2 — add the Expo app to an existing project
If you already have a Makerkit project, the CLI installs the Expo app as a plugin. It copies apps/native, packages/mobile-ui and the /api/v1 routes into your repo, then edits your config files to wire them up.
Before you start
| Requirement | Why |
|---|---|
| Makerkit 4.x | The plugin targets the v4 line. Earlier kits will not take the wiring cleanly. |
The next-supabase variant | The plugin declares no configuration for the Drizzle, Prisma, React Router or TanStack variants, and the CLI refuses to install it on them. |
GitHub access to makerkit/expo-supabase-turbo-saas-kit | The files are fetched from that repository over HTTPS or SSH. |
| A committed working tree | The install edits many files at once. Commit first so you can read the result as a single diff. |
Install
npx @makerkit/cli@latest plugins add expoThe CLI detects your kit variant, then hands off to the codemod that does the work. You can also run that codemod directly, without the CLI:
npx codemod @makerkit/next-supabase-expoRun either from the root of your repository.
To install from a specific branch, tag or commit of the Expo kit rather than main:
EXPO_KIT_REF=v1.2.3 npx codemod @makerkit/next-supabase-expoWhat it adds
| Path | What it is |
|---|---|
apps/native | The Expo app |
packages/mobile-ui | React Native UI components (@kit/mobile-ui) |
apps/web/app/api/v1 | The REST API the app calls, authenticated with a Supabase bearer token |
packages/features/accounts/src/schema/update-personal-account.schema.ts | Shared by PATCH /api/v1/accounts/personal and the native update hook |
tooling/scripts/src/i18n-native.mjs | Generates the app's message catalogue from the web app's |
docs/native | This documentation |
What it changes
These are edits to files you already own. Each one is an AST transform rather than a text replacement, and each is idempotent.
| File | Change |
|---|---|
pnpm-workspace.yaml | Adds catalogs.expo (React Native and Expo SDK versions), overrides, and aes-js, use-intl, @formatjs/intl-pluralrules, @types/aes-js to the root catalog |
package.json | The ten *:native scripts |
apps/web/package.json | Adds @kit/otp, used by /api/v1/otp/send |
apps/web/supabase/config.toml | Allows the mkkit:// OAuth callback |
.oxlintrc.json | Bans web's password schemas inside apps/native; ignores .expo, the generated catalogue and the generated mobile UI components |
.oxfmtrc.jsonc | Stops the formatter fighting the generated catalogue |
tooling/scripts/package.json | i18n:native and i18n:native:check |
packages/features/team-accounts/package.json | Exports the per-seat billing service, which /api/v1 needs |
packages/features/team-accounts/src/server/api.ts | Adds getInvitationById and getPendingInvitationsForEmail |
packages/features/team-accounts/src/shared/types.ts | Adds the PendingInvitation shape |
packages/features/accounts/src/shared/index.ts | Re-exports the personal account schema |
The repo-wide build, typecheck and test turbo tasks are deliberately left alone — the native workspaces join as ordinary workspace members, so the existing tasks already cover them.
What it runs
After the file copy and the config edits, the workflow runs pnpm install, regenerates the message catalogue, then typechecks and formats. The catalogue step matters: apps/native ships a messages.generated.ts built from this kit's web strings, so it is rebuilt against yours. Otherwise your app would render the kit author's copy — a different product name, different wording, and none of your locales. See docs/native/i18n.mdoc.
Every step is idempotent, and the file fetch is skipped entirely when apps/native already exists. Re-running re-applies the config wiring without touching your changes to the app. If the copy is interrupted partway through, it rolls back to the pre-run state rather than leaving a half-installed apps/native that the next run would mistake for a complete one.
After installing
The .env.development that ships with the app points at the standard local ports, so it usually needs no change. Confirm these three match your setup:
EXPO_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321EXPO_PUBLIC_SUPABASE_PUBLIC_KEY=<your local publishable key>EXPO_PUBLIC_API_BASE_URL=http://localhost:3000The post-install output suggests setting EXPO_PUBLIC_API_BASE_URL to http://10.0.2.2:3000 for the Android emulator. That is no longer necessary: apps/native/features/core/dev-host.ts rewrites loopback hosts to 10.0.2.2 automatically when running on Android in development, so the committed value works on both simulators unchanged. A physical device still needs your machine's LAN IP. See docs/native/running-locally.mdoc.
Then restart Supabase so it picks up the new redirect URL, and start the app:
pnpm run supabase:web:resetpnpm run start:nativeSign in with Apple is off by default — it needs a paid Apple Developer account, and enabling it changes what your iOS builds require. See docs/native/config-requirements.mdoc.
Keeping it updated
The Expo plugin is self-distributing: it brings its own files rather than receiving them from the plugin registry, because the registry stores file contents as strings and cannot carry the app's binary assets. One consequence is that makerkit plugins update, plugins outdated and plugins diff do not track it — it is reported as up to date rather than failing. To take a newer version of the app, pull it yourself with EXPO_KIT_REF set to the ref you want, and review the diff.
Which files are yours now
However you installed it, everything under apps/native and packages/mobile-ui is your code to edit. docs/native/project-structure.mdoc explains where your own screens, components and hooks belong, and the one-way import rule that keeps the layers apart.
Read docs/native/running-locally.mdoc next.