A quick introduction to building apps with React Native
A practical intro for React developers who want to ship a mobile app without learning Swift or Kotlin. Expo setup, component differences, navigation, native APIs, and shipping to the stores.

Most React developers who want to ship a mobile app hit the same question early. React Native, Flutter, pure native, or a PWA? This post assumes you chose React Native, and walks through what you actually need to know to ship your first app. Not a 40-screen tutorial. Just the shape of the thing.
Use Expo, not bare React Native
The React Native docs still offer two tracks: bare CLI or Expo. For a new project, choose Expo. It has quietly become the right way to build React Native apps for roughly 95% of cases. The Expo team does the boring work, upgrading native toolchains, shipping over-the-air updates, handling builds, so you do not have to.
Starting is one command:
npx create-expo-app@latest my-app
cd my-app
npx expo startThe template ships with expo-router, which gives you file-based routing that looks exactly like Next.js App Router. If you are comfortable with Next.js, you already know the pattern.
Components are different, the mental model is the same
The biggest shift is that you cannot use <div> and <span>. React Native has its own primitives.
Viewreplacesdiv.Textreplacesspan.ScrollViewreplaces a scrollablediv.PressableandTouchableOpacityreplacebutton.Imagereplacesimg.
Every piece of text has to live inside a Text component. No exceptions. This trips everyone up on day one.
Styling uses a StyleSheet API with a familiar-looking subset of CSS:
import { StyleSheet, Text, View } from "react-native";
export default function Home() {
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome</Text>
<Text style={styles.subtitle}>Tap the screen to start</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: 24,
backgroundColor: "#0a0a0a",
},
title: {
fontSize: 28,
fontWeight: "700",
color: "white",
marginBottom: 8,
},
subtitle: {
fontSize: 14,
color: "rgba(255,255,255,0.7)",
},
});Flexbox is the layout model, and it works almost identically to web flex, with two gotchas. flexDirection defaults to column, not row. And there is no margin collapsing.
Navigation with expo-router
File-based routing makes navigation boring in a good way. A file at app/index.tsx is the home screen. A file at app/settings.tsx is the settings screen. Dynamic routes use [id].tsx, identical to Next.js.
app/
_layout.tsx
index.tsx
settings.tsx
products/
index.tsx
[id].tsxuseRouter and Link from expo-router work like their Next.js siblings. The mental overhead is close to zero if you come from App Router.
State works the way you expect
State is plain React. useState, useReducer, Context, or any library you already like. Zustand works. Jotai works. TanStack Query works.
For simple persistence, use @react-native-async-storage/async-storage, which is a key-value API similar to localStorage. For anything more than preferences, reach for expo-sqlite or op-sqlite.
Native APIs through Expo modules
Most native functionality is wrapped in an Expo module you can install with a single command:
npx expo install expo-camera expo-notifications expo-location expo-secure-storeCamera, notifications, location, secure storage, haptics, audio, video, and file system all have first-party modules. If you need something unusual, Expo config plugins let you extend the native build without ejecting.
Platform differences are real but manageable
iOS and Android are different. Some of the places where it matters:
- Keyboard handling.
KeyboardAvoidingViewis your friend. - Safe area insets. Use
useSafeAreaInsetsfromreact-native-safe-area-context. Not magic numbers. - Status bar colors and styles. Handle them explicitly with
expo-status-bar. - Hardware back button. Android has one, iOS does not. Navigation needs to handle the back press correctly on Android.
Platform.OS === "ios" is fine for small branches. If you find yourself writing platform conditionals everywhere, reconsider the architecture.
Building and shipping with EAS
Expo Application Services handles the builds. One command builds an iOS IPA, another builds an Android AAB:
eas build --platform all
eas submit --platform ios
eas submit --platform androidFor over-the-air updates to JavaScript code, expo-updates lets you ship fixes without going through the app stores. A legitimate shortcut that people underuse.
First submission to the App Store is the worst part. Apple's reviews are inconsistent, the metadata screens are maddening, and the initial screenshot requirements eat a day. Budget for it.
When React Native is the wrong choice
- Graphics-heavy games. Use Unity or native.
- Apps with deep platform integration. Complex CarPlay, widgets with rich interaction, or system-level services are cleaner in native.
- Apps where the UX has to feel 100% platform-native with no seams. React Native is close, but not identical.
For everything else, it is a good choice. Quicktalog's mobile experience is a PWA today, but if we took it fully native tomorrow, React Native is what we would reach for.
Ship a demo, not a tutorial
The fastest way to learn React Native is to ship a tiny real app in a week. A notes app. A habit tracker. Anything with data, navigation, and one native capability like notifications. You will hit every wall once, and learn which ones matter.
If you want a second opinion on a React Native architecture, or on an existing web app you are thinking about porting, we would be glad to take a look.
