LabHub

Blog

Cross-Platform Mobile Development in 2026 — React Native New Architecture / Flutter / KMP / Tauri / Lynx Deep Dive

한국어English日本語

Prologue — 2026, Age of Five Contenders

In 2018, mobile cross-platform had two players: React Native and Flutter. Cordova and Ionic filled the webview niche, and NativeScript sat on the edge.

May 2026 looks completely different.

Five contenders. Above them sit Capacitor 7, .NET MAUI, and NativeScript, all holding their lanes.

This post lays out the 2026 mobile cross-platform map in a single arc — five models, framework deep dives, and what the big Korean and Japanese companies actually ship.


1. The 2026 Cross-Platform Map — Five Contenders

Start with five frameworks on one table. Numbers are pulled from npm, pub.dev, Maven Central downloads, and GitHub stars as of May 2026.

FrameworkLanguageRenderingEmpty-app bundleCore strength
React Native + ExpoTypeScript/JSNative render (Fabric)iOS 15MB / Android 13MBWeb-developer friendly, huge ecosystem
FlutterDartOwn renderer (Impeller)iOS 17MB / Android 14MBPixel-perfect consistency, steady 60fps
Compose Multiplatform + KMPKotlinSkia (Skiko)iOS 18MB / Android 9MBFree choice of shared scope (UI or logic)
Tauri 2 MobileRust + web frontSystem WebViewiOS 8MB / Android 6MBTiny bundles, security, Rust backend
LynxTypeScript/JSDual-thread + own rendernot published (est. 12MB)TikTok-grade perf, dual threads

Three nearby contenders.

FrameworkLanguageRenderingNote
Capacitor 7 (Ionic)TypeScript/JSSystem WebViewIonic team, web-first
.NET MAUIC#/XAMLNative renderMicrosoft camp, enterprise
NativeScriptTypeScript/Vue/SvelteNative render (JS bridge)Small community, still alive

One line to remember: "Rendering model is personality." System WebViews build fast, native render stays consistent, and own-renderers control every pixel.


2. The Four Models of Mobile Cross-Platform (Now Five)

Cross-platform boils down to "how do you draw the native UI?" Five paths.

1) WebView model — Capacitor / Cordova / Tauri Mobile

Layer a web app on top of WKWebView (iOS) and the Android WebView. Camera, Bluetooth, push, etc. are surfaced by native plugins through a JS bridge.

2) Native bridge model — React Native (old architecture) / NativeScript

JS runs in its own engine, native UI (UIView/View) lives separately. They communicate through a JSON message queue.

3) Native render model — React Native New Architecture / .NET MAUI

JS still runs in its own engine, but JSI (JavaScript Interface) enables synchronous calls, and Fabric builds the native view tree directly from C++.

4) Own-renderer model — Flutter / Compose Multiplatform (iOS)

Skip the native UI altogether. Draw every pixel with a graphics library like Skia / Impeller. iOS buttons and Android buttons alike are buttons Flutter draws.

5) Dual-thread plus own renderer — Lynx

ByteDance's new model. A JS thread (main business logic) sits beside a separate UI thread, and the first frame is painted synchronously. Similar to RN's Fabric, only more aggressive about keeping the main thread free.

One line to remember: "WebView is fast to build, native render is consistent, own-renderer controls every pixel."


3. React Native New Architecture — Default Since 0.76

October 2024's 0.76 release was the inflection point. New Architecture became default, and every new project from that day forward ships with Fabric, TurboModules, and Hermes.

Break the three parts down.

Hermes — the JS engine

Built by Facebook. Default since RN 0.70, and effectively the standard RN engine by 2026.

JSC (JavaScriptCore) is kept for compatibility, but new projects choose Hermes. V8 is available as an option on Android only, at the cost of a larger bundle.

JSI — JavaScript Interface

The new bridge between JS and native. Two key facts.

// A synchronous call on JSI (TurboModule)
import { NativeModules } from 'react-native'
const { CryptoModule } = NativeModules

// Before: always async
// const hash = await CryptoModule.sha256(input)

// Today (TurboModule + JSI): sync allowed
const hash = CryptoModule.sha256Sync(input)

TurboModules — the native-module interface

CodegenSchema generates C++/Java/Obj-C interfaces from TypeScript types. The old NativeModules' runtime dynamic dispatch becomes compile-time type-safe.

// specs/NativeCrypto.ts
import type { TurboModule } from 'react-native'
import { TurboModuleRegistry } from 'react-native'

export interface Spec extends TurboModule {
  sha256(input: string): string
  randomBytes(length: number): string
}

export default TurboModuleRegistry.getEnforcing\<Spec\>('Crypto')

This one file emits Android (Java/Kotlin) and iOS (Obj-C/Swift) interface code automatically.

Fabric — the new UI manager

A C++ layer accepts the React tree and constructs the native view tree directly.

Migration checklist

# New project on 0.76+
npx @react-native-community/cli@latest init MyApp  # New Arch on by default

# Migrating an existing project
# 1. Upgrade to RN 0.76+
npx react-native upgrade

# 2. iOS Podfile
# RCT_NEW_ARCH_ENABLED=1 npx pod-install

# 3. Android gradle.properties
# newArchEnabled=true

# 4. Audit incompatible libraries
npx react-native doctor

4. Expo SDK 52 + Expo Router 4 — The De Facto Standard

By 2026 you rarely hear "I start with bare RN." Expo is now the standard RN setup. Three reasons.

  1. EAS Build — cloud builds without local Xcode. iOS certificates and provisioning included.
  2. EAS Update — OTA updates for JS, images, and small assets. Not an App Store dodge, just "ship the bug fix faster."
  3. Expo Router 4 — file-based routing on native, the way Next.js's app/ directory feels on the web.

Expo Router 4 — file-based routing

app/
├── _layout.tsx              # root layout (Tab/Stack)
├── (tabs)/
│   ├── _layout.tsx          # tab layout
│   ├── index.tsx            # /
│   ├── feed.tsx             # /feed
│   └── profile.tsx          # /profile
├── post/
│   └── [id].tsx             # /post/123 (dynamic route)
├── modal.tsx                # /modal (native modal)
└── +not-found.tsx           # 404
// app/_layout.tsx
import { Stack } from 'expo-router'

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
    </Stack>
  )
}
// app/post/[id].tsx
import { useLocalSearchParams } from 'expo-router'
import { Text, View } from 'react-native'

export default function PostScreen() {
  const { id } = useLocalSearchParams\<{ id: string }\>()
  return (
    <View>
      <Text>Post {id}</Text>
    </View>
  )
}

Two key facts.

EAS Build — cloud builds

// eas.json
{
  "cli": { "version": ">= 13.0.0" },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "ios": { "simulator": false }
    },
    "production": {
      "autoIncrement": true
    }
  },
  "submit": {
    "production": {}
  }
}
eas build --platform ios --profile production
eas submit --platform ios --latest

Two lines and you ship to the App Store. Xcode never opens.

EAS Update — OTA

# A small code-only change
eas update --branch production --message "Fix login crash"

If the change doesn't touch native modules, it reaches users without store review. As long as you respect App Store guidelines, this is fine (full UI revamps still warrant a real build).


5. Flutter 3.27 + Impeller — The Own-Renderer Strategy

Flutter took a different road from the start. It doesn't use the OS's native UI. Skia — now Impeller — draws every pixel.

Impeller — the renderer that replaced Skia

Through 2024 the Skia + GPU shader compile pipeline produced first-frame jank. Impeller is the new renderer that fixes it.

Material You 3 + Cupertino

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF6750A4),
        useMaterial3: true,
        brightness: Brightness.light,
      ),
      darkTheme: ThemeData(
        colorSchemeSeed: const Color(0xFF6750A4),
        useMaterial3: true,
        brightness: Brightness.dark,
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Hello Flutter 3.27')),
      body: const Center(child: Text('Material You 3 + Impeller')),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: const Icon(Icons.add),
      ),
    );
  }
}

useMaterial3: true plus colorSchemeSeed is enough to activate the full Material You color system. Light, dark, tone mapping, and accessibility contrast all wire up automatically.

The identity of the own-renderer

State the trade clearly.

Flutter's path is "if we have to diverge from the OS to keep consistency, we will." It fits games, media, and brand-led apps. It feels awkward in apps that need to mirror OS look and feel.


6. Compose Multiplatform 1.7 / KMP 2.1 — iOS Officially Stable

Through 2024, Compose Multiplatform's iOS support was "alpha." Q2 2025 reached beta in 1.6, and Q1 2026 reached stable in 1.7. The implication is big.

The Jetpack Compose code you write runs as-is on iOS.

KMP — the freedom to choose what to share

The core idea of Kotlin Multiplatform is "share whatever you want." Three flavors.

  1. Share logic only — network, DB, domain in KMP. UI stays SwiftUI on iOS, Compose on Android. The most conservative and most adopted pattern.
  2. Logic + some UI — common screens use Compose Multiplatform, platform-specific screens stay native.
  3. Share everything — 100% Compose Multiplatform. Similar to Flutter, an own-renderer on iOS (Skiko, Skia for Kotlin).
// shared/src/commonMain/kotlin/UserRepository.kt
class UserRepository(private val api: UserApi, private val db: UserDb) {
    suspend fun getUser(id: String): User {
        return db.findById(id) ?: api.fetch(id).also { db.insert(it) }
    }
}
// shared/src/commonMain/kotlin/App.kt
@Composable
fun App() {
    MaterialTheme {
        var name by remember { mutableStateOf("World") }
        Column {
            TextField(value = name, onValueChange = { name = it })
            Text("Hello, $name!")
        }
    }
}
// Calling from the iOS app
import shared
import SwiftUI

struct ContentView: View {
    var body: some View {
        ComposeView()  // embed Compose UI inside SwiftUI
    }
}

What's new in KMP 2.1

Compose Multiplatform iOS — where it sits

One line to remember: "KMP lets you start with logic and grow into UI on your own schedule."


7. Tauri 2 Mobile — Rust Plus System WebView, a New Attempt

Tauri found its footing on desktop (Windows / macOS / Linux) as an Electron alternative. Sub-30MB bundles and one-fifth the memory were the selling points. Mobile went from alpha to beta in 2024-2025.

The architecture — Rust core + system WebView

// src-tauri/src/lib.rs
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
// On the front (React) side
import { invoke } from '@tauri-apps/api/core'

const greeting = await invoke\<string\>('greet', { name: 'YJ' })

Tauri 2 Mobile — strengths and weaknesses

Who uses it

In 2026, Tauri Mobile is showing adoption in prosumer tools — secure key managers, local note apps, developer utilities. TikTok-scale consumer apps are still rare.


8. Capacitor 7 vs Cordova — Web-First Today

The two rivals of web-first mobile. Cordova effectively halted major updates in 2024, with the Apache Cordova project entering a retirement track. The web-first slot belongs to Capacitor 7 (built by the Ionic team).

Capacitor 7 essentials

// Capacitor camera usage
import { Camera, CameraResultType } from '@capacitor/camera'

const takePhoto = async () => {
  const photo = await Camera.getPhoto({
    quality: 90,
    allowEditing: false,
    resultType: CameraResultType.Uri,
  })
  return photo.webPath
}
# Basic setup
npm install @capacitor/core @capacitor/cli
npx cap init
npx cap add ios
npx cap add android
npm run build
npx cap copy
npx cap open ios

Who uses it

The App Store rejects thin webview wrappers more often these days, so Capacitor needs to demonstrate "hybrid value" — native features in use, offline behavior, app-like interactions.


9. Lynx (ByteDance) — TikTok's Internal Framework, Open Source

In March 2025 ByteDance open-sourced Lynx, the cross-platform framework powering TikTok, Lark, and Douyin internally. The implication is significant.

Why a new framework?

We already have RN and Flutter. Why? Paraphrasing ByteDance's stated rationale.

A single line of Lynx code

import { View, Text, Image } from '@lynx-js/react'

export default function App() {
  return (
    <View style={{ padding: 20 }}>
      <Text style={{ fontSize: 24 }}>Hello Lynx</Text>
      <Image src="https://example.com/cat.jpg" style={{ width: 100, height: 100 }} />
    </View>
  )
}

On the surface it looks like RN — JSX and components. The differences live in the engine.

What's different

Who is it for?

ByteDance built it for their own apps (TikTok-scale). That means it is tuned for massive traffic + infinite scroll + short videos. For general SaaS, e-commerce, and productivity apps, RN and Flutter remain the safer pick.

The real meaning of open-sourcing it is a public acknowledgment that "there are cases RN and Flutter cannot reach" and a leak of TikTok-grade know-how to the world. Late 2026 and 2027 will tell us who follows.


10. .NET MAUI / NativeScript — The Other Options

Two more options hold their lanes alongside the five contenders.

.NET MAUI — Microsoft's answer

The successor to Xamarin.Forms. C# plus XAML targeting iOS, Android, Windows, and macOS from one codebase.

<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
  <VerticalStackLayout>
    <Label Text="Hello, MAUI!" FontSize="32" />
    <Button x:Name="CounterBtn" Text="Click me" Clicked="OnCounterClicked" />
  </VerticalStackLayout>
</ContentPage>

NativeScript — still alive

A survivor from the Telerik era. TypeScript, Vue, Svelte, or Angular driving real native UI (UIView / View) directly.

In May 2026 NativeScript is mostly used out of nostalgia for the pre-RN era. For new projects, RN, Flutter, or KMP usually come first.


11. How to Choose — A Decision Matrix

Line up the five contenders and two options across team, product, design, and bundle size.

By team

Team backgroundFirst pickSecond pickNote
Web (React/TS)React Native + ExpoCapacitor 7RN has the smoothest ramp
Web (Vue/Svelte)Capacitor 7Tauri 2 MobileWhen RN's React dependency irritates you
Kotlin / AndroidKMP + Compose MultiplatformFlutteriOS rollout can be gradual
iOS-heavyKMP (logic only)React NativeKeep SwiftUI as is
Dart / mobile-firstFlutterCompose MultiplatformPrioritize pixel consistency
C# / .NET.NET MAUIReact NativeEnterprise

By product

Product typeRecommendationWhy
Content / feed (TikTok-grade)Lynx or RNMain-thread protection
Games / interactive mediaFlutterPixel-perfect 60fps
FinTech / bankingRN (Expo) or KMPBig ecosystem, security libraries
E-commerceRN (Expo)Fast launch, ad SDK compatibility
Internal LOB appsCapacitor 7Fast delivery, low cost
Prosumer toolsTauri 2 MobileTiny bundle, security

By design

Design directionRecommendation
Strong OS look and feelRN, KMP (SwiftUI / Compose separately)
Brand pixel-perfectFlutter, Compose Multiplatform
Reuse web design directlyCapacitor, Tauri Mobile

By bundle size

FrameworkiOS empty appAndroid empty app
Tauri 2 Mobileabout 8MBabout 6MB
Capacitor 7about 10MBabout 8MB
KMP (logic sharing)about 12MBabout 7MB
React Native + Expoabout 15MBabout 13MB
Flutterabout 17MBabout 14MB
Compose Multiplatformabout 18MBabout 9MB

These are empty-app sizes. Real apps balloon two or three times with images, fonts, and libraries. Past 100MB you hit the App Store cellular-download cap and lose automatic 4G/5G installs.


12. Korean and Japanese Mobile Ecosystems — What They Actually Use

The theory table is the starting point; reality is different. Here's what big Korean and Japanese companies actually shipped as of May 2026 (based on public hiring posts, engineering blogs, and conference talks).

Korea

Japan

Patterns across both markets

Takeaway

Big companies adopt cross-platform only when "risk < code-sharing benefit." They keep native for apps where the core experience matters, and validate cross-platform in new features, experiments, and internal tools. KMP's logic-only sharing is the safest entry point.


Wrap-up — Checklist and Anti-Patterns

Decision checklist

  1. What is the team's primary language? (TS / Dart / Kotlin / Swift / C#)
  2. Is the design baseline OS-standard or brand pixel-perfect?
  3. Are the hero screens 60fps video and complex animation, or forms and lists?
  4. Do you have a bundle-size cap? (App Store cellular cap is 200MB)
  5. What is the balance between App Store / Play Store review speed and OTA updates?
  6. Do you already own web assets? (Capacitor / Tauri value increases)
  7. What are the security demands? (Tauri's Rust backend, or RN's security libraries)
  8. Who maintains the code five years from now?

Ten anti-patterns

  1. Running RN 0.76+ with the New Architecture disabled — unnecessary asymmetry.
  2. Starting on Expo and then ejecting and regretting it.
  3. Burning time making Flutter mimic native iOS look and feel.
  4. Copying the pattern of putting KMP UI sharing in production before stable (pre-2025).
  5. Forcing 60fps game animations on top of Capacitor.
  6. Trying to ship a TikTok-scale consumer app on Tauri Mobile.
  7. Adopting Lynx for small internal tools — overkill.
  8. Pinning the JS engine to JSC and never migrating to Hermes.
  9. Using EAS Update for a full UI redesign and risking App Store guidelines.
  10. Choosing .NET MAUI for a mobile-first design app and pushing desktop patterns on it.

Next-up ideas

Candidate follow-ups: React Native New Architecture migration in practice — a 0.74 to 0.76 walkthrough, Keep both SwiftUI and Compose alive with KMP logic-only sharing, Debugging Flutter Impeller shaders — taming the jank.

"Cross-platform isn't the magic of writing one codebase for two OSes — it's a negotiation about which costs you move and where."

— Cross-Platform Mobile Development 2026, fin.


References

Comments

No comments yet.

Sign in to leave a comment