Skip to Content

Upgrade from v1

Use this guide to move an existing NativeExpress 1.x app to v2.0.0 while preserving your custom code and data.

This migration is optional. New projects already use v2 and do not need these steps.

Review this NativeExpress 1.x app against the released v2.0.0 source. Identify custom screens, integrations, native configuration and database changes to preserve. Propose an upgrade plan and a staging verification checklist before editing. Do not reset a database or deploy changes.

Before you start

Save a working baseline

Commit your current app and create an upgrade branch. Keep a working build and its configuration. Check your custom screens, sign-in, purchases, push notifications and stored data before making changes, then use the same list to verify the upgrade.

Back up your database and storage objects. Rehearse database changes on a staging project with representative data, including any custom tables and policies.

Update dependencies and styling

Compare your configuration with v2.0.0 before installing dependencies. Merge the changes to package.json, yarn.lock, metro.config.js, babel.config.js and tsconfig.json, keeping any configuration your app needs.

In your 1.x appWhat to update
Expo and React Native dependenciesUse the v2 dependency set for Expo SDK 57, React Native 0.86, React 19.2 and TypeScript 6. Keep compatible dependencies you added.
NativeWindReplace its Metro wrapper with withUniwindConfig. Remove nativewind/babel and the NativeWind JSX import source from Babel.
tailwind.config.jsMove custom theme values into src/global.css and src/theme/themes/mono.css. Tailwind v4 configuration is defined in CSS.
nativewind-env.d.tsRemove the file and its tsconfig.json entry. Use v2’s uniwind-types.d.ts.
@/components/ui/...Replace the removed GlueStack wrappers with React Native primitives or heroui-native components.
Screens using SafeAreaViewFollow v2’s safe-area setup in src/app/_layout.tsx and use inset utilities such as <View className="pt-safe">. The boilerplate’s lint rules disallow SafeAreaView imports.
Custom fonts and colorsPreserve your branding while adapting it to the v2 theme. The supplied fonts are Geist and Instrument Serif.
Type-only importsUse import type; v2 enables verbatimModuleSyntax.

Keep the complete theme variable set in both light and dark modes. Use DESIGN.md and the bundled Uniwind and HeroUI Native skills when adapting screens. The root layout also adds providers and Stack.Protected route guards, so merge your routes into that setup and check signed-in and signed-out navigation.

The demo product screens, Product model, product repository and hooks are removed. Keep your implementations and translation keys if your app still uses them.

Update integrations and AI

Review each integration’s imports, arguments and return values.

Existing codev2 replacement
@/hooks/analytics/*analytics from @/services/analytics; replace ConditionalPostHogProvider with AnalyticsProvider. See analytics usage.
@/lib/one-signalFunctions from @/services/push. Permission is requested through onboarding. See push notifications.
RevenueCatContext purchase methodsThe context now exposes isPro, isConfigured, identifyUser and logout. packages, customerInfo, purchasePackage and restorePermission are removed.
Paywall presentationUse presentPaywall from @/services/paywall for the onboarding_end and quota_exceeded placements. Keep PaywallProvider in the root layout.
Direct Sentry callsUse logError from @/lib/logger. See monitoring.
src/lib/env.tsRead your app’s process.env.EXPO_PUBLIC_* variables directly.
call-llm and useCallLLMAdapt callers to the Chat, Create or Scan feature you need. These have different request and response shapes.

The Paywall component at @/components/paywall/Paywall provides purchase and restore UI. It takes an onClose callback and an optional onPurchaseError callback. If you enable Superwall, complete its purchase and subscription integration before testing purchases.

The AI functions use OpenRouter. Set OPENROUTER_API_KEY as a Supabase function secret and configure models in supabase/functions/_utils/ai.config.json. The new functions do not accept a user’s own provider key.

Pro access depends on an active RevenueCat entitlement. Set config.jspurchases.proEntitlementId and the server’s PRO_ENTITLEMENT_ID to the same entitlement; both default to pro. Configure REVENUECAT_SECRET_KEY before testing Pro-only AI features. See function secrets and quota for the settings shared by the app and server.

Prepare a migration for your database

v2 ships one initial migration, supabase/migrations/20260904000000_initial_schema.sql, for new projects. It replaces the initial migration shipped with 1.x.

Do not apply the v2 initial migration to a database that already ran the 1.x migrations or reset it to make it fit. Keep your migration history and write a new migration for the changes your app needs.

Compare your actual schema with the released v2 migration. Review these changes:

AreaWhat to preserve or add
Custom dataKeep your tables, relationships, triggers and policies. The 1.x data, categories and data_categories tables are absent from the new initial schema; their data is not converted to AI records.
AI featuresAdd the threads, messages, generations and scans tables, their indexes and owner policies when adopting the corresponding features.
StorageAdd the private chat-media bucket and its policies. Review existing avatar paths before adopting policies that require avatars/{userId}.<ext> inside the avatars bucket.
UsageAdd count_monthly_usage with its ownership check and execution grants. The released function permits authenticated and service_role, with access revoked from public and anon.

Keep your custom bucket configuration if you need it, or migrate its objects and references before switching to v2’s fixed avatars bucket. The new app does not read EXPO_PUBLIC_SUPABASE_BUCKET_NAME.

Apply your migration to staging, then regenerate src/lib/db/database.types.ts from that project. Follow type definitions. Deploy the new edge functions after their database dependencies exist. Keep call-llm deployed while any installed app versions still call it.

Rebuild the app

Preserve your bundle identifiers, Android package name, app scheme and EAS project ID when merging config.js and app.config.js. Check your Google sign-in settings and any native plugins you added.

The v2 eas.json selects an environment for each build profile and uses remote versioning with production autoIncrement. Keep the version sequence required by your existing store apps. Set their submission identifiers and credentials. Add your EXPO_PUBLIC_* variables to the matching EAS environments; a local .env.local file does not configure a cloud build. See production builds.

Run yarn install and create a new development build for each platform you ship. Preserve any native changes before regenerating the native projects.

Verify

Run yarn ci and review the testing guide, then check the upgraded app on your supported platforms:

  • Follow your baseline feature list, including custom screens in light and dark modes.
  • Install over an existing test build and check that sign-in survives. v2 migrates the existing Supabase session from AsyncStorage to SecureStore.
  • Check existing database records and images, then confirm that one test account cannot access another account’s private data.
  • Test purchases and restores with sandbox products. Confirm that Pro access agrees in the app and the AI functions.
  • Test chat, an attachment, a scan and a free-quota refusal if you adopted those features.

Repeat the rehearsed database, function and app release steps in production only after staging passes. Keep the previous app and backend behavior available for installed clients until your rollout no longer depends on them.

Last updated on