Skip to content
Mehedi Hasan Sagor

// blog

Cross‑Platform Mobile Development with Ionic and React Native: A Pragmatic Guide

Mehedi Hasan Sagor Nov 6, 2026 5 min read
Cross‑Platform Mobile Development with Ionic and React Native: A Pragmatic Guide

Why Pair Ionic and React Native?

Both Ionic and React Native promise "write once, run everywhere," but they solve different problems. Ionic renders a web view using standard HTML, CSS and JavaScript (or TypeScript) on top of Capacitor/Cordova. React Native, on the other hand, compiles to native UI components via a JavaScript bridge. For many startups the decision is binary – pick one and commit. However, a hybrid approach can give you the best of both worlds:

  • Rapid UI prototyping – Ionic’s component library and CSS utilities let you spin up screens in hours.
  • Native‑look‑and‑feel for performance‑critical screens – React Native excels at fluid animations, complex gestures, and heavy list rendering.
  • Shared business logic – Validation, API clients, and state management can live in a single TypeScript codebase.

When you have a product that needs a polished onboarding flow, a marketing‑driven web‑like experience, and a few performance‑sensitive features (e.g., a real‑time chat), mixing the two frameworks can reduce development time while keeping the user experience high.

Core Differences and When Each Shines

| Aspect | Ionic | React Native | |--------|-------|--------------| | UI Rendering | Web view (Chromium) – HTML/CSS | Native components via bridge | | Performance | Adequate for most forms, lists, and static content | Superior for animations, large lists, complex gestures | | Ecosystem | Huge collection of web UI kits, easy theming with CSS variables | Rich native module ecosystem, direct access to platform APIs | | Learning Curve | Familiar to web developers; uses Angular, React, or Vue | Requires knowledge of React and native concepts (e.g., useNativeDriver) | | Platform Parity | Near‑identical UI across platforms | Slight UI differences due to native components |

A practical rule of thumb: use Ionic for screens that look like a web page or need fast iteration, and switch to React Native for screens where native performance matters. The challenge is keeping the hand‑off smooth.

Sharing Code Between the Two Stacks

1. Put Business Logic in a Pure TypeScript Library

Create a libs/shared folder that contains services, models, validation, and API clients. Because both Ionic and React Native run JavaScript, you can import the same files without modification.

// libs/shared/apiClient.ts
import axios from 'axios';

export const api = axios.create({
  baseURL: process.env.API_URL,
  timeout: 8000,
});

export async function fetchUser(id: string) {
  const { data } = await api.get(`/users/${id}`);
  return data;
}

Both projects can import fetchUser directly, ensuring a single source of truth for network calls.

2. Share UI with Stencil Web Components

If you need a component that looks identical in both environments, Stencil can compile a web component that works inside an Ionic web view and inside React Native via react-native-webview.

// components/my-button.tsx (Stencil)
import { Component, Prop, h } from '@stencil/core';

@Component({ tag: 'my-button', styleUrl: 'my-button.css', shadow: true })
export class MyButton {
  @Prop() label: string = 'Click';
  @Prop() onClick: () => void;

  render() {
    return (
      <button onClick={this.onClick}> {this.label} </button>
    );
  }
}

In Ionic you simply use <my-button label="Save" />. In React Native you embed it:

// RNScreen.tsx
import { WebView } from 'react-native-webview';

export default function RNScreen() {
  const html = `<my-button label="Save" onclick="window.ReactNativeWebView.postMessage('save')"></my-button>`;
  return <WebView source={{ html }} onMessage={e => console.log(e.nativeEvent.data)} />;
}

While this adds a small overhead, it avoids duplicating simple UI elements and keeps branding consistent.

3. Use Capacitor Plugins for Native Features

Capacitor plugins are JavaScript‑first but expose native code for iOS and Android. They work equally well with Ionic and React Native because you can call them from any JavaScript context.

// libs/shared/camera.ts
import { Camera, CameraResultType } from '@capacitor/camera';

export async function takePhoto() {
  const photo = await Camera.getPhoto({
    quality: 90,
    resultType: CameraResultType.Uri,
  });
  return photo.webPath; // works in both web view and RN WebView
}

If you need a truly native implementation that React Native cannot reach via Capacitor, write a thin bridge module and expose the same API surface.

Project Structure & CI/CD

A monorepo approach keeps everything in sync and simplifies versioning.

my-app/
├─ packages/
│  ├─ ionic/          # Ionic app (Capacitor)
│  ├─ react-native/   # RN app (Expo or bare)
│  └─ shared/         # TS library, Stencil components
├─ .github/workflows/
│  ├─ ios.yml
│  └─ android.yml
└─ lerna.json        # optional, for package linking

Key CI steps:

  1. Lint & TypeCheck – Run eslint and tsc --noEmit on the whole repo.
  2. Unit Tests – Use Jest for shared code, @testing-library/react for RN, and @ionic/angular testing utilities for Ionic.
  3. Build Stencil – npm run build:stencil produces a distribution folder that both apps import.
  4. App Builds – Separate jobs for ionic:android, ionic:ios, rn:android, rn:ios. Cache node_modules and Gradle/Xcode builds to speed up pipelines.
  5. Deploy – Use Fastlane for store uploads; the same Fastfile can handle both platforms with different lanes.

Because the shared library is versioned together, any change to an API automatically triggers both app builds, preventing drift.

Practical Checklist Before You Ship

  • Component parity: Verify that every shared UI component renders correctly in a real device test for both stacks.
  • Performance profiling: Use Chrome DevTools for Ionic screens and react-native-performance for RN screens. Pay special attention to large lists (ion-virtual-scroll vs FlatList).
  • Native permission handling: Centralize permission requests in the shared library using Capacitor; map the result to RN’s PermissionsAndroid when needed.
  • Deep linking: Implement a universal link handler in both apps that forwards to the same routing logic (e.g., using react-router in Ionic and react-navigation in RN).
  • Analytics: Choose a provider that works in a web view and native (e.g., Segment) and initialise it once in the shared code.
  • App size audit: Run npx react-native-bundle-visualizer and ionic build --prod to identify duplicated assets. Remove any unused Ionic components from the RN build.
  • Automated UI tests: Cypress for Ionic (web) and Detox for RN. Keep the test scripts in e2e/ and run them on CI.
  • Version bump policy: When you add a new shared API, bump the monorepo version and tag a release; CI should automatically publish the updated packages to your private npm registry.

By following this workflow you get a single source of truth for business logic, consistent branding, and optimal performance where it matters. The result is a faster time‑to‑market for startups that need both a web‑like experience and native‑grade interactions.

Takeaway – Treat Ionic and React Native as complementary layers rather than competing choices. Build shared TypeScript services, leverage Stencil for reusable UI, and orchestrate everything in a monorepo with a robust CI pipeline. The payoff is a leaner codebase, lower maintenance cost, and a product that feels native on both iOS and Android without sacrificing development velocity.

  • Ionic
  • React Native
  • Architecture
  • Mobile
  • Cross Platform