React Native

React Native lets you build a genuinely separate native app -- one that compiles down to real iOS and Android views, not a browser rendering HTML -- while reusing the React mental model you already use in Next.js: components, props, state, hooks, and a declarative render function. It is the option on the "going mobile" spectrum that involves the most new code, but it's also the only one that gives you a fully native look, feel, and performance profile.

Same mental model, different rendering target

This is the single most important thing to understand about React Native: it is React, but it is not the DOM. A Next.js component renders to HTML elements like <div> and <p>, which the browser paints. A React Native component renders to native platform views -- <View> and <Text> instead of <div> and <p> -- which iOS and Android render using their own native UI toolkits. There is no HTML, no CSS in the browser sense (styling uses a JavaScript object syntax modeled on CSS, applied via StyleSheet), and no DOM APIs like document or window.

The same conditional-rendering, useState, and useEffect patterns you already use in a Next.js Client Component carry over directly -- only the JSX tags and styling API change.
app,(tabs),index.tsx (Next.js Client Component):
'use client'
import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)
  return (
    <div style={{ padding: 16 }}>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  )
}

screens,Counter.tsx (React Native):
import { useState } from 'react'
import { View, Text, Pressable, StyleSheet } from 'react-native'

export default function Counter() {
  const [count, setCount] = useState(0)
  return (
    <View style={styles.container}>
      <Text>Count: {count}</Text>
      <Pressable onPress={() => setCount(count + 1)}>
        <Text>Increment</Text>
      </Pressable>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { padding: 16 },
})


Expo: the common way to start today

Expo is a toolchain and set of managed services built on top of React Native that handles the native build process, over-the-air updates, and a large library of pre-built native modules (camera, location, notifications, and more) without requiring you to touch Xcode or Android Studio for most day-to-day work. Starting a new React Native project today almost always means running Expo's CLI rather than configuring the bare React Native toolchain by hand -- it's the closest equivalent to how create-next-app gets you started on the web side.

Expo Router, in particular, is worth calling out because it deliberately mirrors Next.js's file-system based App Router: routes are files under an app/ folder, nested folders create nested routes, and files named [param].tsx create dynamic segments. The routing concepts transfer almost directly; the actual screen components underneath still render React Native primitives, not HTML.


terminal:
npx create-expo-app@latest my-app --template tabs
cd my-app
npx expo start


What can realistically be shared with your Next.js app

"Learn once, write anywhere" was React Native's original tagline, and it's more accurate than "write once, run anywhere" -- expect to share knowledge and some logic, not entire screens. In practice:

Can typically be sharedBusiness logic, validation functions, date/formatting utilities, TypeScript types, an API client built on fetch, state management stores (Zustand, Redux), and any hook that doesn't touch the DOM or browser-only globals.
Cannot be sharedAnything rendering JSX for the DOM (your Next.js pages, layouts, and components), Server Components, Server Actions, Route Handlers, and any Next.js routing/config file -- these have no equivalent runtime in React Native, which has no server and no HTML.
Shareable with adaptationHooks that call browser APIs (localStorage, window) need a React Native equivalent (AsyncStorage, Dimensions) behind the same interface; styling has to be reimplemented per platform since there is no CSS.

The most effective way to maximize sharing is to keep an isolated, framework-agnostic package (in a monorepo, for example) for your business logic and API layer, and treat both the Next.js app and the React Native app as thin UI shells that import from it -- rather than trying to share components directly, which rarely works cleanly given how different the two render targets are.



When to reach for this instead of a WebView or Capacitor

Building a real React Native app is the right call when you need native navigation gestures and animations, tight integration with native APIs (camera, Bluetooth, background tasks, push notifications) beyond what a plugin bridge exposes, or the best possible runtime performance for complex UI. If you mainly want an app-store presence for content you've already built as a Next.js site, the lighter-weight React Native WebView or Capacitor approaches get you there with far less new code.