Skip to content
Legacy Modernisation

Migrating from Xamarin or PhoneGap to React Native: When It's the Right Move

11 min read Matt Hammond

React Native is the right migration target when you want a broader ecosystem, stronger community support, or when the existing app is Xamarin.Native or PhoneGap (where MAUI is not a viable option). The migration involves a language change, but the business logic is preservable and the result is a more maintainable, more hireable, better-supported mobile stack.

  • React Native with Expo is our recommended default for new enterprise mobile projects and migrations.
  • Business logic (API clients, data models, validation) can be translated from C# or JavaScript to TypeScript with AI assistance.
  • The UI layer is a rebuild, not a translation, because the rendering models are fundamentally different.
  • Expo simplifies the build, deployment, and update lifecycle significantly compared to both Xamarin and PhoneGap.
  • Budget 6-12 weeks for a mid-complexity app, depending on screen count and native integration complexity.

When React Native is the right target

This guide is for teams that have decided (or are deciding) that React Native is the right migration target. If you are still choosing between MAUI and React Native, read our comparison guide first.

React Native is the right choice for:

PhoneGap and Cordova apps. There is no direct migration path from PhoneGap to MAUI. React Native is the natural successor: it runs native components (not a WebView), has a mature plugin ecosystem, and uses JavaScript/TypeScript (which PhoneGap developers already know).

Xamarin.Native apps (Xamarin.iOS / Xamarin.Android). These apps used platform-specific APIs directly, without the Xamarin.Forms abstraction layer. Migrating to MAUI offers no structural advantage because MAUI is the successor to Xamarin.Forms, not Xamarin.Native. The migration effort to either MAUI or React Native is comparable, so choose based on ecosystem and hiring factors.

Teams investing in web and mobile. If your organisation also builds web applications in React, sharing code and knowledge between web and mobile teams is a practical benefit. Component patterns, state management approaches, and TypeScript skills transfer directly.

Hiring-constrained teams. The React/React Native developer market is significantly larger than the MAUI developer market. If recruiting speed matters, React Native has an advantage.

Architecture mapping: where your code lands

Understanding how the old architecture maps to React Native helps plan the migration.

From Xamarin

Xamarin conceptReact Native equivalent
ViewModel (MVVM)Custom Hook or Context Provider
ContentPage (XAML)Screen Component (JSX/TSX)
NavigationPage / ShellReact Navigation (Stack, Tab, Drawer)
DependencyServiceReact Context or module imports
MessagingCenterEvent emitters, Zustand, or React Query
Xamarin.Essentialsexpo-* packages (camera, location, etc.)
Custom RendererNative Module or Expo Module
Data binding (XAML)Props, state, and React hooks

From PhoneGap/Cordova

PhoneGap conceptReact Native equivalent
HTML pageScreen Component (JSX/TSX)
CSS stylingStyleSheet or NativeWind
Cordova pluginExpo module or React Native library
cordova.exec()Native Module bridge (TurboModule)
WebView renderingNative component rendering
deviceready eventuseEffect hook in root component
LocalStorageAsyncStorage or expo-secure-store
Ajax/fetchfetch API (same, but typed with TypeScript)

Project setup with Expo

We recommend Expo for all new React Native projects. The historical trade-off (Expo was simpler but limited) no longer applies. Expo’s development builds support custom native modules, and EAS Build handles the iOS and Android build pipelines.

npx create-expo-app MyApp --template expo-template-blank-typescript
cd MyApp

Project structure

MyApp/
├── app/                    # Screens (using Expo Router)
│   ├── (tabs)/            # Tab-based navigation group
│   │   ├── index.tsx      # Home screen
│   │   ├── orders.tsx     # Orders screen
│   │   └── profile.tsx    # Profile screen
│   ├── order/
│   │   └── [id].tsx       # Order detail (dynamic route)
│   └── _layout.tsx        # Root layout
├── components/            # Shared UI components
├── hooks/                 # Custom hooks (business logic)
├── services/              # API clients and external integrations
├── models/                # TypeScript types and interfaces
├── stores/                # State management (Zustand)
└── utils/                 # Pure utility functions

Expo Router provides file-based routing similar to Next.js. This is a significant improvement over both Xamarin’s navigation stack and PhoneGap’s URL-based routing.

Migrating business logic

Business logic is the most preservable layer in any migration. API clients, data transformation functions, validation rules, and domain models can be translated to TypeScript.

C# to TypeScript (from Xamarin)

// BEFORE: Xamarin C# model and service
public class Order
{
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public DateTime OrderDate { get; set; }
    public List<OrderLine> Lines { get; set; }
    public decimal Total => Lines?.Sum(l => l.Quantity * l.UnitPrice) ?? 0;
}

public class OrderService
{
    private readonly HttpClient _client;

    public async Task<List<Order>> GetOrdersAsync(int customerId)
    {
        var response = await _client.GetAsync(
            $"api/orders?customerId={customerId}");
        response.EnsureSuccessStatusCode();
        var json = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<List<Order>>(json);
    }
}
// AFTER: React Native TypeScript (generated by Claude Code)
interface OrderLine {
  quantity: number;
  unitPrice: number;
}

interface Order {
  id: number;
  customerName: string;
  orderDate: string;
  lines: OrderLine[];
}

function calculateTotal(order: Order): number {
  return order.lines.reduce(
    (sum, line) => sum + line.quantity * line.unitPrice,
    0
  );
}

async function getOrders(customerId: number): Promise<Order[]> {
  const response = await fetch(
    `${API_BASE_URL}/api/orders?customerId=${customerId}`
  );
  if (!response.ok) {
    throw new Error(`Failed to fetch orders: ${response.status}`);
  }
  return response.json();
}

Claude Code handles this translation accurately for data models, API clients, and utility functions. The type mapping (C# decimal to TypeScript number, C# DateTime to TypeScript string for JSON, C# List<T> to TypeScript T[]) is well-defined.

JavaScript to TypeScript (from PhoneGap)

PhoneGap apps written in JavaScript benefit significantly from TypeScript migration. Claude Code adds types to untyped JavaScript, catches potential null reference issues, and structures the code into typed modules.

// BEFORE: PhoneGap JavaScript
function getOrders(customerId, callback) {
  $.ajax({
    url: API_URL + '/api/orders?customerId=' + customerId,
    method: 'GET',
    success: function(data) { callback(null, data); },
    error: function(err) { callback(err, null); }
  });
}
// AFTER: React Native TypeScript with React Query
import { useQuery } from '@tanstack/react-query';

function useOrders(customerId: number) {
  return useQuery({
    queryKey: ['orders', customerId],
    queryFn: async () => {
      const response = await fetch(
        `${API_BASE_URL}/api/orders?customerId=${customerId}`
      );
      if (!response.ok) throw new Error('Failed to fetch orders');
      return response.json() as Promise<Order[]>;
    },
  });
}

The pattern shift from callback-based jQuery to React Query hooks is significant but well-suited to AI-assisted translation. Claude Code understands both paradigms and generates idiomatic React Native code.

Migrating the UI layer

The UI layer is where the migration shifts from translation to rebuild. Neither Xamarin XAML nor PhoneGap HTML maps directly to React Native JSX.

Approach: screen-by-screen rebuild

Rather than attempting to translate UI markup mechanically, we approach the UI migration as a screen-by-screen rebuild using the existing app as a visual specification:

  1. Screenshot every screen in the existing app (both platforms)
  2. Build a component library of shared UI primitives (buttons, inputs, cards, lists)
  3. Rebuild each screen using the component library, matching the existing design
  4. Review on devices for visual parity

AI tooling helps here by generating component scaffolding from screen descriptions and by producing styled components that match the existing design system. But the visual fidelity check is a human activity.

Styling

React Native does not use CSS. It uses a subset of CSS properties expressed as JavaScript objects:

import { StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#ffffff',
  },
  title: {
    fontSize: 24,
    fontWeight: '600',
    marginBottom: 8,
  },
});

For teams that prefer a CSS-like workflow, NativeWind (Tailwind CSS for React Native) provides utility classes:

<View className="flex-1 p-4 bg-white">
  <Text className="text-2xl font-semibold mb-2">Order Details</Text>
</View>

Plugin migration

Both Xamarin and PhoneGap relied heavily on plugins for device features. React Native’s ecosystem provides equivalents for virtually all common plugins.

CapabilityExpo PackageNotes
Cameraexpo-cameraPhoto and video capture
Locationexpo-locationForeground and background tracking
File systemexpo-file-systemRead/write/download files
Push notificationsexpo-notificationsLocal and remote push
Biometricsexpo-local-authenticationFace ID, Touch ID, fingerprint
Secure storageexpo-secure-storeEncrypted key-value storage
Image pickerexpo-image-pickerCamera roll and camera access
Mapsreact-native-mapsGoogle Maps / Apple Maps
Barcode scanningexpo-barcode-scannerQR and barcode scanning

For plugins without Expo equivalents, React Native’s native module system (TurboModules in the New Architecture) allows you to write platform-specific code in Swift/Kotlin and expose it to JavaScript.

Build and deployment with EAS

Expo Application Services (EAS) replaces the build and deployment complexity that both Xamarin and PhoneGap required:

EAS Build compiles your app in the cloud. No local Xcode or Android Studio configuration needed for CI/CD. Builds are triggered from the command line or from GitHub Actions.

EAS Submit handles App Store and Play Store submission directly.

EAS Update enables over-the-air JavaScript bundle updates without going through store review. This is a significant advantage for hot-fixing issues in production.

# Build for both platforms
eas build --platform all

# Submit to stores
eas submit --platform ios
eas submit --platform android

# Push an OTA update (JS-only changes)
eas update --branch production --message "Fix order total calculation"

This is a dramatic simplification compared to Xamarin’s MSBuild-based build chain or PhoneGap Build (which was discontinued).

What we have seen in practice

[CLIENT EXAMPLE: Field services company, PhoneGap app for job scheduling with camera capture, offline data sync, and GPS tracking (~20k LOC JavaScript). Migrated to React Native with Expo over 5 weeks. Business logic (scheduling algorithms, API clients, validation) was converted to TypeScript with ~55% AI-authored code. UI was rebuilt screen-by-screen using NativeWind. Push notifications reimplemented with expo-notifications (an improvement over the Cordova plugin). Offline sync rebuilt with WatermelonDB. Key improvement: EAS Update enables same-day fixes without store review, which was impossible with PhoneGap.]

[CLIENT EXAMPLE: Property management company, Xamarin.Native app (separate iOS and Android codebases) with 35 screens, camera/document scanning, and Bluetooth LE for smart locks. Migrated to React Native over 10 weeks. The separate platform codebases were consolidated into a single React Native codebase. Bluetooth LE integration used react-native-ble-plx. Document scanning used expo-camera with custom image processing. AI-assisted development handled the C# to TypeScript business logic translation (~50% AI-authored). Key lesson: consolidating two platform-specific codebases into one React Native codebase cut the ongoing maintenance surface in half.]

[CLIENT EXAMPLE: Retail company, Xamarin.Forms loyalty app with 22 screens, barcode scanning, push notifications, and in-app payments. Evaluated for both MAUI and React Native. Chose React Native because the team wanted to hire React developers for their web platform simultaneously. 7-week migration with Expo. Payment integration used Stripe’s React Native SDK (a significant improvement over the Xamarin Stripe binding). AI-assisted tooling handled data model translation and API client generation. Total AI-authored code: ~50%.]

Timeline expectations

App complexityScreensNative integrationsTypical timeline
SimpleUnder 151-2 (camera, location)4-6 weeks
Moderate15-403-5 (push, biometrics, offline)6-10 weeks
Complex40-805+ (Bluetooth, payments, AR)10-14 weeks
Very complex80+Custom native modules14+ weeks

These timelines include AI-assisted development. Without AI tooling, add 40-60% to the business logic translation and component scaffolding phases.

Next steps

If you are evaluating React Native as a migration target, start with the mobile migration playbook for the full assessment framework.

For the comparison with MAUI, see our honest enterprise comparison.

For IP considerations around AI-generated code, see Who Owns AI-Written Code?.

For the general modernisation decision framework, see Modernise, Rebuild, or Replace.

Ready to plan your migration? Book a free Mobile Migration Assessment consultation.

Frequently asked questions

Is React Native production-ready for enterprise apps?
Yes. React Native powers production apps at Meta, Microsoft, Shopify, Bloomberg, and many enterprise organisations. The New Architecture (Fabric renderer, TurboModules) released in recent versions has significantly improved performance and eliminated many of the historical concerns about bridge overhead.
Should we use Expo or bare React Native?
We recommend Expo for most enterprise projects. Expo provides a managed build system (EAS Build), over-the-air updates (EAS Update), simplified native module management, and a consistent development experience. The historical limitation of Expo (no custom native modules) was resolved with Expo's 'development builds' feature.
How do we handle our C# backend if the mobile app moves to React Native?
The backend does not change. React Native calls your existing .NET API via HTTP (REST or GraphQL) exactly as your Xamarin or PhoneGap app does. The mobile migration is a client-side concern. If your API contracts are well-defined, the backend is not affected.
Can our C# developers learn React Native?
The language change (C# to TypeScript) is the biggest adjustment. Developers experienced with MVVM patterns adapt to React's component model fairly quickly. The bigger learning curve is the React ecosystem (state management, navigation, build tooling). Budget 2-4 weeks for a C# developer to become productive in React Native.
How does AI help with a migration that involves a language change?
Claude Code can translate business logic from C# to TypeScript and from JavaScript to typed React Native patterns. It handles data models, API client generation, and utility function conversion well. The UI layer typically needs more human input because the component models are fundamentally different.

Ready to transform your software?

Let's talk about your project. Contact us for a free consultation and see how we can deliver a business-critical solution at startup speed.