React Native for React Developers: Getting Started
I map out what transfers from React to React Native — JSX, hooks, props — and what changes — native views, StyleSheet, the flexbox defaults — so you can build your first mobile screen with confidence.

If you’ve been writing React on the web, the first React Native project you open will feel surprisingly familiar. And then you’ll spot <View> instead of <div>, look for a CSS file that doesn’t exist, and wonder where the DOM went.
The disorientation is real — but it’s narrower than it looks. The programming model is identical. What changed is the render target.
I’m going to map out exactly what transfers from your React knowledge, what requires adjustment, and which workflow gets you to a running screen with the least friction.
This is the first post in an eight-part series on building apps with React Native. We’ll go deeper on navigation, native APIs, performance, and publishing in the parts that follow — but this one is about getting your bearings first.
Series: Part 1 of 8.
On this page
- What your React knowledge already covers
- From DOM to native views: the component swap
- StyleSheet vs CSS: same concept, different syntax
- The Flexbox default that breaks layouts on day one
- Expo vs bare workflow: which starting point to choose
- Writing your first React Native screen
- The shift that makes the rest of this series easier
What your React knowledge already covers
The React Native documentation states this clearly: React Native runs on React, the same library. When you open a React Native project, you’re still writing React components, still calling the same hooks, still passing props down and handling events up.
Here’s what carries over unchanged:
- JSX syntax — the exact same angle-bracket notation, the same rules about single root elements and fragments, the same
{}curly-brace interpolation - All hooks —
useState,useEffect,useCallback,useMemo,useRef,useContext, and any custom hooks you’ve written - Props and one-way data flow — pass them down, read them in children, lift state to share across siblings
- Component composition — nest components the same way, compose UI from small pieces
- TypeScript for props and state — the same interface and type syntax, the same inference
The React Fundamentals guide covers these patterns in detail if you want a refresher before continuing. Everything described there — components as functions, how state triggers re-renders, the one-way data flow rule — applies identically in React Native. The practical hooks guide is relevant too. The useEffect cleanup contract, the stale closure trap, the rules about calling hooks at the top level — all of it carries directly across.
The JavaScript execution model works the same way inside React Native’s JS engine. Component functions close over their lexical scope, the call stack behaves as expected, and the event loop drives updates.
If your React knowledge is solid, roughly 70–80% of what you’ll do daily in React Native will feel familiar.
From DOM to native views: the component swap
Here’s where things change. In React for the web, your components ultimately produce HTML elements — div, p, span, img, input. The browser renders those into the DOM.
React Native doesn’t have a DOM. At runtime, it converts your components into native platform views — UIView on iOS, ViewGroup on Android. This is why React Native apps look and feel like actual native apps rather than wrapped websites.
The core component mapping is:
| Web | React Native | What it renders to |
|---|---|---|
<div> |
<View> |
UIView (iOS) · ViewGroup (Android) |
<p>, <span> |
<Text> |
UITextView (iOS) · TextView (Android) |
<img> |
<Image> |
UIImageView (iOS) · ImageView (Android) |
<input type="text"> |
<TextInput> |
UITextField (iOS) · EditText (Android) |
Scrollable <div> |
<ScrollView> |
UIScrollView (iOS) · ScrollView (Android) |
The rule that catches every React developer on day one: all text must live inside a <Text> component. You can’t place a bare string directly in a <View>. The renderer validates this at runtime, and the error message isn’t always obvious the first time you see it.
// This throws at runtime — no bare strings in View
<View>
Something will break here
</View>
// Correct — text always lives inside Text
<View>
<Text>This works</Text>
</View>
Once you know the swap, it becomes second nature. A <View> behaves like a <div>: it’s a container, it supports flexbox layout, and it holds other components. A <Text> behaves like a <p> or <span>: it renders text, and text-related styling goes here rather than on the container.
StyleSheet vs CSS: same concept, different syntax
React Native uses JavaScript objects for styles. There are no external CSS files, no stylesheets linked from HTML. The property names match CSS concepts but are written in camelCase — backgroundColor instead of background-color, borderRadius instead of border-radius.
You define styles with StyleSheet.create():
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#ffffff',
},
heading: {
fontSize: 24,
fontWeight: 'bold',
color: '#1a1a1a',
},
});
The React Native documentation describes the style system as intentionally matching CSS property names where possible. Most values you know — padding, margin, borderWidth, opacity — work the same way. Colour values accept hex strings, rgb(), and named colours. Numbers without units are treated as density-independent pixels.
Two meaningful differences from CSS:
No cascade. Styles don’t inherit down the component tree the way CSS does. A fontSize set on a <View> doesn’t apply to <Text> children automatically. Each component needs its own explicit style. This feels restrictive at first, but it makes components easier to reason about in isolation — you always know where a style comes from.
No class selectors. There are no CSS classes, no specificity rules, no global selector conflicts. Every style is a plain JavaScript object applied directly via the style prop. For larger codebases this simplicity is genuinely useful — there’s no global stylesheet to audit when something looks wrong.
The Flexbox default that breaks layouts on day one
React Native uses Flexbox for layout — the same algorithm you know from CSS. But the defaults are different, and the one you’ll encounter first is:
flexDirection defaults to 'column' in React Native. In CSS, the default is row.
This means items stack vertically by default. When you write two <View> components inside a container expecting them to appear side by side, they stack on top of each other. You need to set flexDirection: 'row' explicitly to get horizontal layout.
Other defaults that differ from CSS:
flexShrinkis0by default in React Native (CSS defaults to1)alignContentis'flex-start'by default (CSS defaults to'stretch')
The flexDirection one is the one you’ll encounter in the first hour. The others tend to surface when you’re building something more complex, but they’re worth knowing before a layout looks inexplicably wrong.
Check this before moving on
- You know that
flexDirectiondefaults to'column', not'row' - You understand that styles don’t cascade down the component tree
- You can name the five core component substitutions for HTML elements
Expo vs bare workflow: which starting point to choose
When you start a new React Native project, you choose between two paths.
Expo — run npx create-expo-app and you get a project that can preview on your physical device immediately, without configuring Xcode or Android Studio. Expo handles the native build layer. You see your app by scanning a QR code in the Expo Go app or by creating a development build — your own build of the app with Expo’s developer tools included.
Bare React Native — the official React Native CLI setup. You configure and maintain Xcode and Android Studio yourself, manage native code directly, and have complete control over every dependency.
For a React developer building their first mobile app, Expo is the right starting point. The feedback loop is fast — a change in your editor reflects on your device in seconds. The tooling is approachable. You can always move to the bare workflow later if you hit a genuine ceiling.
One distinction worth knowing early: Expo Go is a sandbox app that works well for learning and prototyping. It doesn’t support every native module. Production apps should use Expo development builds, which support custom native modules that the Expo Go sandbox can’t run. The two approaches to setup are described clearly in the Expo environment docs.
Writing your first React Native screen
Here’s a minimal screen that shows the familiar and the different side by side:
import { StyleSheet, View, Text, TextInput, TouchableOpacity } from 'react-native';
import { useState } from 'react';
export default function NotesScreen() {
const [note, setNote] = useState('');
return (
<View style={styles.container}>
<Text style={styles.label}>Add a note</Text>
<TextInput
style={styles.input}
placeholder="Type something..."
value={note}
onChangeText={setNote}
multiline
/>
{note.length > 0 && (
<Text style={styles.preview}>{note}</Text>
)}
<TouchableOpacity
style={styles.button}
onPress={() => setNote('')}
>
<Text style={styles.buttonText}>Clear</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: '#ffffff',
},
label: {
fontSize: 18,
fontWeight: '600',
marginBottom: 12,
},
input: {
borderWidth: 1,
borderColor: '#d0d0d0',
borderRadius: 8,
padding: 12,
fontSize: 16,
minHeight: 80,
},
preview: {
marginTop: 16,
fontSize: 16,
color: '#555555',
},
button: {
marginTop: 16,
backgroundColor: '#1a1a2e',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignSelf: 'flex-start',
},
buttonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
},
});
What’s identical to React: useState works exactly the same way. Conditional rendering with && behaves identically. Component composition follows the same rules. The TypeScript syntax for typing props and state — interfaces, generics, type inference — carries over unchanged.
Two differences worth calling out:
onChangeText replaces the onChange + e.target.value pattern. React Native passes the updated string value directly, not a synthetic event object. So setNote receives the string immediately — no .target.value needed.
TouchableOpacity is the standard wrapper for tappable elements. In React for the web, you can attach onClick to any <div>. In React Native, <View> doesn’t respond to taps by default — you wrap tappable content in <TouchableOpacity> (or <Pressable>, the newer and more flexible option that replaced it in most cases). The onPress prop replaces onClick.

The render target changes — browser DOM on the left, native views on the right — but the component structure and programming model stay the same.
The shift that makes the rest of this series easier
Once you stop thinking of React Native as “mobile development” and start thinking of it as React with a different render target, most of the unfamiliar parts become predictable.
The render target changes the component names, the styling approach, and the navigation library. It doesn’t change how you structure components, manage state, or reason about data flow. The programming model you know from React carries intact.
The practical lesson: when you encounter a React Native API you haven’t seen before, your React instincts about props, state, and composition will guide you correctly. The native-specific parts are narrower than they first appear — and they become less surprising with each screen you build.
Start with Expo, build one screen, and let the hooks you already know do most of the work.