# 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.

*Canonical: https://makerkit.dev/docs/react-native-supabase/installation*

---

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.

{% alert type="info" title="The Expo kit is not offered by `makerkit new`" %}
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.
{% /alert %}

```bash
git clone https://github.com/makerkit/expo-supabase-turbo-saas-kit.git my-app
cd my-app
pnpm install
```

Point `origin` at your own repository:

```bash
git remote set-url origin https://github.com/<you>/<your-repo>.git
git push -u origin main
```

Then 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:

```bash
pnpm run supabase:web:start   # local Supabase
pnpm run dev                  # the Next.js app on :3000
pnpm run ios:native           # or: pnpm run android:native
```

The 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

```bash
npx @makerkit/cli@latest plugins add expo
```

The 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:

```bash
npx codemod @makerkit/next-supabase-expo
```

Run either from the root of your repository.

To install from a specific branch, tag or commit of the Expo kit rather than `main`:

```bash
EXPO_KIT_REF=v1.2.3 npx codemod @makerkit/next-supabase-expo
```

### What 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`.

{% alert type="info" title="Re-running is safe" %}
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.
{% /alert %}

### 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:

```bash
EXPO_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
EXPO_PUBLIC_SUPABASE_PUBLIC_KEY=<your local publishable key>
EXPO_PUBLIC_API_BASE_URL=http://localhost:3000
```

{% alert type="info" title="You do not need a per-device API URL in development" %}
The 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`.
{% /alert %}

Then restart Supabase so it picks up the new redirect URL, and start the app:

```bash
pnpm run supabase:web:reset
pnpm run start:native
```

Sign 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.
