Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
Mariano Ksairi avatar

Expo Rn Plugin

  • Updated August 3, 2026
  • ksairi-org/claude

expo-rn-plugin is a Claude Code skill in the Frontend Development category. Claude Code plugin for React Native / Expo projects. MCP servers, scaffolding, Figma sync, i18n review. Supports Supabase, Firebase, and REST backends.

Key points

  • expo-rn-plugin
  • Frontend Development
  • AI-coding skill

Expo Rn Plugin by the numbers

  • Data as of Aug 4, 2026 (Skillselion catalog sync)
/plugin marketplace add ksairi-org/claude
/plugin install expo-rn-plugin@ksairi-org

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Last updatedAugust 3, 2026
Repositoryksairi-org/claude

What it does

Claude Code plugin for React Native / Expo projects. MCP servers, scaffolding, Figma sync, i18n review. Supports Supabase, Firebase, and REST backends.

README.md

expo-rn-plugin

Claude Code plugin for React Native / Expo projects. Provides MCP servers, scaffolding skills, Figma sync, i18n review, and TypeScript code intelligence — all in one installable plugin. Supports Supabase, Firebase, and REST backends.

Requirements

  • macOS or Linux (Windows: WSL2)
  • Node.js 18+
  • Python 3 (python3) — used by mcp-run.sh and guard-generated-files.sh to parse JSON
  • Yarn Berry (corepack enable && corepack prepare yarn@stable --activate)
  • Homebrew — setup-app.sh installs jq and doppler via brew
  • Claude Code CLI

New app quickstart

Before step 3: you need a Doppler account and workspace. If you haven't set it up yet:

  1. Create a free account and workspace at doppler.com
  2. Run doppler login in your project folder — this authenticates the CLI and links the folder to your workspace

setup-app.sh then runs the service wizard interactively to collect credentials for each optional MCP (Doppler, Supabase, Figma, Sentry, Stripe, Firebase). Skip any you don't need yet — their MCP servers will show red until configured, which is intentional. Re-run setup-app.sh any time to fill in more.

CLAUDE_PLUGIN_ROOT is set automatically by the marketplace installer inside claude sessions. When testing from source with --plugin-dir, it is not set automatically — prefix the command as shown below.

# 0. Pre-requisites (one-time, outside the project)
#    - Create a Doppler account + workspace at doppler.com
#    - doppler login   ← authenticate the CLI in your project folder

# 1. Create your Expo app
yarn create expo-app my-app && cd my-app

# 2. Install the plugin
claude plugin install expo-rn-plugin --scope project
# Testing from source? Set CLAUDE_PLUGIN_ROOT explicitly:
#   CLAUDE_PLUGIN_ROOT=/path/to/expo-rn-plugin claude --plugin-dir /path/to/expo-rn-plugin

# 3. Run one-time setup — interactive, takes ~2 min
bash "${CLAUDE_PLUGIN_ROOT}/scripts/setup-app.sh"
# When testing from source (CLAUDE_PLUGIN_ROOT not set), run directly:
#   bash /path/to/expo-rn-plugin/scripts/setup-app.sh
# → Copies CLAUDE.md, mcp.config.json, .claude/settings.json, .claude/commands/
# → Auto-fills CLAUDE.md with project name from package.json
# → Detects actual dir structure and writes mcp.config.json
# → Adds sync-env-vars + sync-design-tokens to package.json; wires prestart
# → Scaffolds env.template.yaml; ensures .env is gitignored
# → Installs typescript-language-server and typescript
# → Runs doppler setup (interactive); auto-fills Figma file ID,
#    Supabase project ref, and Sentry project into CLAUDE.md

# 4. Edit CLAUDE.md — in the "## Project context" section, fill in:
#    - api: https://api.your-domain.com
#    (project name, Figma file ID, Supabase ref, Sentry project are auto-filled)

# 5. Start Claude
claude

Production setup (new app)

Delegate as much as possible to EAS — it handles certificates, provisioning profiles, App Store app creation, and keystore management automatically.

i18n compiled catalogs: do NOT gitignore src/i18n/locales/compiled/. EAS local builds archive the project via git — gitignored files never reach the build temp directory, causing Metro to fail resolving locales/compiled/en. Commit the compiled output. Add "postinstall": "yarn i18n:compile" as a safety net for local dev.

1. iOS — App Store Connect API key (do this first)

Create an App Store Connect API key with App Manager role and give it to EAS:

eas credentials --platform ios
# Choose: "Add new App Store Connect API Key" → paste Key ID + Issuer ID + upload .p8

With this in place, EAS will automatically:

  • Register the bundle ID in the Apple Developer portal
  • Create the app in App Store Connect (sets ascAppId in eas.json)
  • Generate and manage provisioning profiles and certificates
  • No Xcode or manual portal work needed

The key is team-wide, so a single upload covers every app and build profile (stg, prd, preview, …). It's also what keeps local and CI builds non-interactive: EAS authenticates to Apple with the key instead of an Apple ID session, so you never get a 2FA device-code prompt or a "select a provisioning profile" prompt — and unlike a cached Apple session, the key doesn't expire monthly. Because the key lives on EAS, both eas build --local on your machine and the GitHub Actions workflow pick it up automatically via EXPO_TOKEN — no per-environment Apple secrets required.

Fallback: if you can't use an API key, you can authenticate with an Apple ID + app-specific password (EXPO_APPLE_ID / EXPO_APPLE_APP_SPECIFIC_PASSWORD), with a device-code prompt on first run. The API key is strongly preferred — the app-specific password path triggers periodic 2FA and is being phased out by Apple.

2. Android — let EAS manage the keystore

On first eas build --platform android, EAS generates and stores the keystore automatically. Always run credentials through Doppler so EAS picks up the correct bundle ID:

doppler run --project mobile --config stg -- eas credentials
doppler run --project mobile --config prd -- eas credentials

⚠️ Never run eas credentials without Doppler — the local .env may point to the wrong bundle ID, corrupting credential associations.

Register TWO SHA-1 fingerprints in Firebase, not one:

Google Play re-signs all AABs with its own App Signing Key. The SHA-1 on the device is not the EAS upload keystore SHA-1. You need both registered:

SHA-1 Where to find it Needed for
Upload keystore SHA-1 eas credentials output Dev client + CI upload builds
Play App Signing SHA-1 Play Console → Setup → App Integrity → App Signing Store-distributed builds

Add both to Firebase Console → Project Settings → Your Android App → Add fingerprint, then re-download and commit google-services.json. Firebase creates a separate Android OAuth client (type 1) for each — both entries appear in google-services.json.

Apple Sign-In on Android — assetlinks.json required:

@invertase/react-native-apple-authentication uses Chrome Custom Tabs to intercept the OAuth callback. Release/store builds enforce Android App Links (Digital Asset Links) verification. Without assetlinks.json on the callback domain, the Chrome Custom Tab never returns to the app.

Serve the following at https://<your-callback-domain>/.well-known/assetlinks.json:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourapp.stg",
      "sha256_cert_fingerprints": ["<PLAY_APP_SIGNING_SHA256_STG>"]
    }
  },
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourapp.prod",
      "sha256_cert_fingerprints": ["<PLAY_APP_SIGNING_SHA256_PRD>"]
    }
  }
]

The SHA-256 fingerprints come from Google Play Console → Setup → App Integrity → App Signing certificate (different from SHA-1). Use Play App Signing SHA-256, not the upload cert.

Keep a reference copy in docs/android/assetlinks.json in the app repo. Add /assetlinks.json to .gitignore to avoid committing the working copy.

3. What still requires manual steps

Step Where
Apple Merchant ID (Apple Pay) developer.apple.com → Identifiers → Merchant IDs
RC iOS In-App Purchase key App Store Connect → Users & Access → Integrations → In-App Purchase
RC Android service account Google Cloud Console → IAM → Service Accounts → download JSON → Play Console → Users & permissions → grant access
RC paywalls app.revenuecat.com → Paywalls → New (dashboard only, no API)

4. RevenueCat — platform-specific API keys

RC requires separate public SDK keys per platform. Use Platform.OS in configureRevenueCat:

// src/services/revenue-cat/index.ts
const apiKey =
  Platform.OS === 'android' && process.env.EXPO_PUBLIC_RC_ANDROID_API_KEY
    ? process.env.EXPO_PUBLIC_RC_ANDROID_API_KEY
    : process.env.EXPO_PUBLIC_RC_API_KEY

Set both REVENUECAT_API_KEY (iOS appl_…) and REVENUECAT_ANDROID_API_KEY (Android goog_…) in Doppler prd. Stg can use a single test_… key for both platforms.

5. Release workflow

Script Output What it does
yarn dev-client-ios / yarn dev-client-android sim-dev-client-stg.{tar.gz,apk} Dev client for simulator / emulator (stg)
yarn dev-client-ios:prd / yarn dev-client-android:prd sim-dev-client-prd.{tar.gz,apk} Dev client for simulator / emulator (prd — real purchases, real auth)
yarn dev-client-ios-device / yarn dev-client-android-device device-dev-client-stg.{ipa,apk} Dev client for physical device (stg)
yarn dev-client-ios-device:prd / yarn dev-client-android-device:prd device-dev-client-prd.{ipa,apk} Dev client for physical device (prd)
yarn build-sim / yarn build-sim:prd sim-app-build-{stg,prd}.tar.gz Bundled iOS app for simulator — no dev server needed; use for splash/startup testing (OTA excluded — sim can't test OTA)
yarn build-ipa / yarn build-ipa:prd device-app-build-{stg,prd}.ipa Bundled IPA for iOS device — required for OTA testing
yarn build-apk / yarn build-apk:prd device-app-build-{stg,prd}.apk Bundled APK for Android device/emulator — required for OTA testing
yarn push-ota / yarn push-ota:prd Export JS bundle and push as an OTA update — requires build-ipa or build-apk binary installed, no dev server running
yarn build-store-ios / yarn build-store-android store-build-stg.{ipa,aab} Store build for stg — submits to TestFlight / Play internal track
yarn build-store-ios:prd / yarn build-store-android:prd store-build-prd.{ipa,aab} Store build for prd without submitting
yarn deploy-store-all:prd Recommended release flow — build prd + submit to TestFlight / Play internal testing for final verification with real purchases; promote to production from the console

Dev-client builds append a -<timestamp> to their output filename (e.g. sim-dev-client-stg-20260101-143022.tar.gz) so re-running never overwrites a previously working artifact. Store/submission builds (build-store-*) use a stable filename (store-build-stg.{ipa,aab}) that is overwritten on each run, so deploy-store-* always submits exactly the artifact that was just built.

Installing iOS simulator builds: EAS local builds for the simulator are .tar.gz archives containing the .app bundle. Extract and install the latest with:

tar -xf "$(ls -t sim-dev-client-stg-*.tar.gz | head -1)"
xcrun simctl install booted *.app
rm -rf *.app

Recommended release steps:

# 1. Build + submit to internal testing (real purchases, closed testers)
yarn deploy-store-all:prd

# 2. Testers verify on TestFlight / Play internal track

# 3. Promote to production — from the console, not CLI
#    Android: Play Console → Internal testing → Promote release → Production
#    iOS: App Store Connect → TestFlight build → Submit for Review

Fill in YOUR_STG_ASC_APP_ID and YOUR_PRD_ASC_APP_ID in eas.json with the numeric App IDs from App Store Connect → App Information.

CI / GitHub Actions

The workflow templates (.github/workflows/) wire up automatic deploys: push to stg → deploy stg builds, push to main → deploy prd builds. Manual dispatch also supported.

Required GitHub secrets

Secret Where to get it
EXPO_TOKEN expo.dev → Account Settings → Access Tokens
DOPPLER_TOKEN Doppler → project → Access → Service Tokens
GOOGLE_PLAY_SERVICE_ACCOUNT_JSON Google Cloud → Service Accounts → JSON key (EAS submit role only)

iOS Apple auth needs no secret when the App Store Connect API key is stored on EAS (the recommended setup above) — CI authenticates with the key via EXPO_TOKEN.

Secret (iOS fallback only) Where to get it
EXPO_APPLE_ID Your Apple ID email
EXPO_APPLE_APP_SPECIFIC_PASSWORD appleid.apple.com → App-Specific Passwords

Only add the two fallback secrets (and uncomment them in expo-ios-deploy.yml) if you are not using an ASC API key on EAS.

Org-level secrets need explicit repo access — go to org Settings → Secrets → grant the repo. A secret set at org level is not automatically inherited by private repos.

Xcode version

The Expo SDK version dictates the minimum Xcode. Running expo-doctor in CI will report the required version. SDK 55 requires Xcode ≥ 26.0 — using an older Xcode causes Swift incompatibilities in expo-modules-core and pre-built XCFrameworks. Set xcode-version: '26.0' (or newer) in the iOS workflow.

i18n compiled catalogs

EAS local builds archive the project via git, so gitignored files are excluded from the build temp directory. Commit src/i18n/locales/compiled/ — do not gitignore it. The compiled .ts files are small and deterministic; excluding them causes Metro to fail resolving locales/compiled/en at bundle time.

expo-doctor in pre-build

Do NOT include yarn doctor in the pre-build script. expo-doctor runs environment-specific checks (network, tool availability) that fail in CI. Keep it as a standalone yarn doctor command for local use only.

Install (existing project)

claude plugin install expo-rn-plugin --scope project
bash "${CLAUDE_PLUGIN_ROOT}/scripts/setup-app.sh"

MCP servers ship with pre-built dist/ — no build step required after install.

Updating the plugin

When the plugin is installed at project scope (the default for app repos, recorded with --scope project), the /plugin UI's "Update" action does not apply to it — that flow targets user-scoped plugins, so a project pin shows as "installed" with no Update button no matter how many times you refresh the marketplace. Use the CLI instead, and always pass the matching scope (claude plugin update defaults to --scope user):

# 1. Refresh the marketplace catalog from its source
claude plugin marketplace update ksairi-org

# 2. Update the plugin at the SAME scope it was installed (project for app repos)
claude plugin update expo-rn-plugin@ksairi-org --scope project

# 3. Restart Claude Code — the plugin is loaded at session start,
#    so the running session keeps the old version until you reload.

Verify the active version with claude plugin list.

Manual fallback (if the CLI can't resolve, e.g. offline): the marketplace fetch lands a copy under ~/.claude/plugins/cache/ksairi-org/expo-rn-plugin/<version>/. Point the project's entry in ~/.claude/plugins/installed_plugins.json at that path — update installPath, version, and gitCommitSha (the marketplace HEAD from git -C ~/.claude/plugins/marketplaces/ksairi-org rev-parse HEAD) — then restart.

Distinct from Updating existing apps below, which covers propagating plugin changes into an app already built from it.

Plugin components

Skills (invoke with /expo-rn-plugin:<name>)

All skills are invoked with the full /expo-rn-plugin:<name> prefix.

Skill Description
scaffold <table> Generate full CRUD (types, hooks, screens, routes, form) from a database table
form <feature> Generate a zod schema, react-hook-form hook, and Tamagui form component
figma <url_or_node_id> Compare screen implementation against Figma design and fix discrepancies
sentry Sentry error monitoring — setup, capture patterns, and MCP usage
stripe Stripe payments — PaymentSheet flow, PCI rules, and MCP usage
preview Screenshot the running simulator, check device errors, and run tsc — use after every UI change
coding-standards Load project coding standards on demand (TypeScript, Tamagui, Zustand, Lingui)
analytics Load analytics standards — event naming, screen tracking, user identification, privacy rules (Firebase default; PostHog, Amplitude alternatives)
testing Write or fix component and hook tests using jest-expo and @testing-library/react-native
animations Animation standards — react-native-reanimated for UI motion, Rive for illustration/splash, rules for when to use each
libs (optional) Full reference for @ksairi-org/* libraries fetched live from GitHub. Load before writing any utility, hook, or layout code if your project uses these packages.
iap Mobile in-app purchases and subscriptions via RevenueCat — setup, customer identification, product configuration for App Store and Google Play
feature-flags Feature flags via Firebase Remote Config — naming conventions, per-environment values, gradual rollouts, and reading values in the app
push-notifications FCM push notifications — setup, token registration, permission flow, daily reminders, and MCP tooling (inspect_push_tokens, send_test_push)
ota Self-hosted OTA updates via Supabase — Edge Function manifest server, upload script, CI workflow, Doppler config. No EAS Update subscription needed.
upgrade-sdk Expo SDK major-upgrade playbook — deps/patches, expo-router/react-navigation migration, stale-dev-client crash, and native verification

Project commands (standalone, no skill file)

These commands are copied to .claude/commands/ by setup-app.sh and are available as /command-name after setup. They are self-contained guides — no skill prefix needed.

Command Description
/auth <google|apple|email|all> Wire up Supabase auth (Google, Apple, email sign-in)
/zustand Canonical Zustand store pattern (typed, MMKV-persisted)
/doppler <VAR=value> Add a new secret to Doppler and sync it to .env
/orval Regenerate OpenAPI hooks from the backend spec
/notifications Set up push notifications (expo-notifications + FCM)
/sync-tokens Pull latest design tokens from Figma mid-session

Agents (available in /agents)

Agent Model Description
expo-scaffolder Haiku Scaffolding specialist — delegates heavy CRUD generation out of main context
database-specialist Opus DB queries, migrations, RLS policies
i18n-reviewer Haiku Audit Lingui catalogs for missing translations and hardcoded strings
auth-specialist Opus Supabase auth flows, Google/Apple sign-in, token lifecycle
payment-specialist Opus Stripe PaymentSheet, PCI compliance, webhooks

Models are declared as aliases (opus / haiku), not pinned snapshots, so agents track the latest model automatically. Reasoning-heavy agents (auth, database, payment) run on Opus with the effort field as the depth/cost dial; mechanical agents (scaffolder, i18n) stay on Haiku. Opus fast mode (/fast) speeds up the interactive scaffold and Figma loops without downgrading the model.

MCP Servers

Server Description
expo React Native / Expo tools: config, routes, components, scaffolding, i18n, EAS, push notifications
database DB introspection, query generation, migration generation, RLS inspection
figma Figma design data and asset export
github Official GitHub MCP server (remote api.githubcopilot.com) — PRs, issues, code search
sentry Error monitoring
stripe Stripe API access
doppler Secret management
firebase Firebase services
context7 Up-to-date library docs (React Native, Expo, etc.)

All stdio servers that require secrets are wrapped via Doppler (bin/mcp-run.sh). The github server is the official remote server and authenticates with a GITHUB_PERSONAL_ACCESS_TOKEN env var (it replaced the now-archived @modelcontextprotocol/server-github). figma still uses the third-party figma-developer-mcp because the design-token sync (figma/sync-figma-tokens.sh) depends on its tool surface — evaluate Figma's official Dev Mode MCP before switching.

setup-app.sh also commits a copy of .mcp.json and bin/mcp-run.sh into each app so the servers come up in Claude Code on the web, where the marketplace plugin isn't installed.

Hooks (automatic)

Event Hook Effect
SessionStart figma/sync-figma-tokens.sh Syncs Tamagui design tokens from Figma if FIGMA_FILE_ID + FIGMA_API_KEY are set (no-op otherwise)
PreToolUse (Write/Edit) guard-generated-files.sh Blocks edits to auto-generated files (src/api/generated/, src/theme/) — run the generator instead
PostToolUse (Write/Edit) tsc-check.sh Runs tsc --noEmit after edits to TypeScript/JS files (skips markdown, JSON, and assets)
PreCompact precompact-state.sh Re-surfaces unapplied Supabase migrations into context so they survive a compaction
Stop context-warning.sh Warns when context window ≥ 70% — prompts for /compact

Monitors (automatic)

Monitor When active Effect
pending-migrations Always Emits a warning when Supabase migration files are unapplied; re-checks every 5 min
eas-active-builds Always (no-ops when eas CLI absent or no active builds) Polls EAS for in-progress builds and prints status updates every 60 s

LSP

TypeScript Language Server (typescript-language-server) — provides go-to-definition, find references, and live diagnostics for .ts, .tsx, .js, .jsx files.

setup-app.sh installs typescript-language-server and typescript as devDependencies automatically.

Design-token tooling (manual, designer-side)

These ship with the plugin but are run by hand outside Claude — they are the human side of the Figma → Tamagui token pipeline, not part of the automated surface above.

Tool Where it runs Purpose
tools/figma-tamagui-sync CLI (auto) Pulls Figma variables into src/theme/. Driven by the SessionStart hook and yarn sync-design-tokens
figma/figma-seed-plugin/ Figma app plugin One-time seed of a blank Figma file with a generic starter token set as Figma Variables
figma/figma-export-plugin/ Figma app plugin Designer exports edited Variables back to JSON for the developer to commit

The starter token set in figma-seed-plugin is a generic example palette — customize the values to match your brand before seeding a real project.

Configuration

The plugin has two optional install-time config keys:

Key Description
doppler_project Your Doppler project name (e.g. my-app)
doppler_config Config to use (dev / stg / prd, default: dev)

You do not need to fill these in manually. setup-app.sh runs doppler setup interactively and writes both values to mcp.config.json automatically. The install-time prompts are a fallback only.

mcp.config.json

mcp.config.json (at your app root) tells the MCP servers where to find your project's files and secrets. setup-app.sh auto-detects most values, but you can edit it at any time:

{
  "doppler": { "project": "my-app", "config": "dev" },
  "database": { "schema": "api" },
  "routesDir": "app",
  "components": {
    "atoms": "src/components/atoms",
    "molecules": "src/components/molecules",
    "organisms": "src/components/organisms",
    "screens": "src/screens"
  },
  "orval": { "sdkLib": "src/api/generated" }
}

The doppler block is what connects MCP servers to your secrets — without it, servers that need env vars (Sentry, Stripe, etc.) will start without credentials.

Doppler setup

Doppler stores all secrets (API keys, Supabase URLs, etc.) so nothing lives in .env files checked into git.

  1. Create a free account at doppler.com if you don't have one
  2. Create a project (e.g. my-app) with a dev config
  3. Add these secrets to the dev config:
    • FIGMA_API_KEY, FIGMA_FILE_ID
    • SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY (EXPO_PUBLIC_SUPABASE_URL and EXPO_PUBLIC_SUPABASE_ANON_KEY are derived automatically in env.template.yaml — never expose the service role key client-side)
    • SUPABASE_ACCESS_TOKEN — personal access token from supabase.com/dashboard/account/tokens; used by the Supabase MCP server to manage projects (different from the service role key)
    • Optional: SENTRY_DSN, SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT
    • Optional: STRIPE_PUBLISHABLE_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
  4. When setup-app.sh runs doppler setup, select your project and dev config

After setup, yarn start automatically writes .env from Doppler secrets via the prestart script. Design tokens also sync on yarn start (via doppler run inside the script — no extra flags needed).

Social auth (Apple + Google via Supabase)

Configure in Supabase → Authentication → Providers for each environment (stg + prd separately).

Apple

  • Client IDs: comma-separated bundle ID + Services ID (e.g. com.myapp.stg, com.myapp.stg.sign-in)
  • Secret Key: generate a .p8 key in Apple Developer → Keys
  • ⚠️ Apple OAuth secret keys expire every 6 months — set a calendar reminder; expired keys silently break web OAuth sign-in
  • Callback URL shown by Supabase → register it in Apple Developer Center → your Services ID → Return URLs

Google

  • Client IDs: your Web client ID (from Google Cloud Console → OAuth 2.0 Client IDs)
  • Client Secret: Web client's secret
  • Enable "Skip nonce checks" — required for native iOS since @react-native-google-signin doesn't pass the nonce back to Supabase
  • Callback URL shown by Supabase → add it to Google Cloud → OAuth → Authorised redirect URIs

Doppler keys required per env (stg + prd)

GOOGLE_WEB_CLIENT_ID              # Web OAuth client ID
GOOGLE_IOS_CLIENT_ID              # iOS OAuth client ID
GOOGLE_ANDROID_CLIENT_ID          # Android OAuth client ID
ANDROID_APPLE_SIGN_IN_CLIENT_ID   # Apple Services ID (Android OAuth)
ANDROID_APPLE_SIGN_IN_CALLBACK    # Supabase callback URL

Supabase URL Configuration

In Authentication → URL Configuration, set the redirect URL per env:

  • stg: {slug}-stg://
  • prd: {slug}://

Updating existing apps

When the plugin updates, apps built from it don't auto-update. Apply changes manually:

  1. MCP servers — rebuild if mcps/*/src/ changed: cd mcps/expo-mcp-server && yarn build
  2. Scripts — re-run setup-app.sh to merge updated package.json scripts; review the git diff before committing
  3. Templates — compare templates/ files against your project manually (no auto-merge); key files to check: app/_layout.tsx, app.config.ts, .gitignore, eas.json
  4. tsconfig paths — add new aliases manually (e.g. @sentry, @fonts) when adopting new services
  5. CLAUDE.md — pull in new "Never do" / "Always do" rules from templates/CLAUDE.md
  6. Skills — always up to date automatically (loaded fresh each session from the plugin root)

Reference implementations for patterns not covered here:

Project CLAUDE.md

Keep your project's CLAUDE.md lean (under 80 lines). Move detailed standards to the on-demand skill:

# Project

React Native / Expo app. For coding standards, run `/expo-rn-plugin:coding-standards`.

## Project Context

- Expo Router for navigation
- Tamagui for styling (`src/theme/`)
- Lingui for i18n
- Database `api` schema (not public)

This keeps session startup context small and only loads standards when needed.

Cost tips

  • coding-standards skill loads on demand — not burned on every session
  • expo-scaffolder and i18n-reviewer use Haiku — cheap for high-volume generation/audit tasks
  • context-warning hook reminds you to /compact at 70% context — prevents wasteful re-reads
  • Install RTK (rtk init -g) — wraps all CLI commands to strip verbose output, cutting per-command token usage by 60–90%

Development

First-time setup (contributors)

Install Claude Code plugins (compound-engineering, expo, github) — run from the plugin root:

bash scripts/setup-claude.sh

Build MCP servers manually

cd mcps/expo-mcp-server && yarn install --immutable && yarn build
cd mcps/database-mcp-server && yarn install --immutable && yarn build

dist/ is committed to git. CI will fail if you push source changes without rebuilding. The pre-push hook handles this for you automatically.

Testing the plugin locally

CLAUDE_PLUGIN_ROOT=$(pwd) claude --plugin-dir .

Validate the manifest

claude plugin validate

Adding a new MCP server

  1. Create a directory under mcps/ (e.g. mcps/my-mcp-server/)
  2. Follow the structure of mcps/expo-mcp-server/ (src/index.ts, src/tools/, package.json, tsconfig.json)
  3. Add an entry to .mcp.json using ${CLAUDE_PLUGIN_ROOT}/bin/mcp-run.sh as the command
  4. Add build_server "my-mcp-server" to scripts/build-mcp-servers.sh — the script hardcodes server names, it does not auto-discover new ones

Releases / versioning

The version is bumped automatically by CI — any push to main that touches plugins/expo-rn-plugin/** patch-bumps .claude-plugin/plugin.json and commits it back, which is what makes the update available to consumers (plugin update). Don't hand-edit the version for routine changes; only bump manually for an intentional minor/major (CI detects that and skips, so there's no double-bump).

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.