from browser to native app
Anyone who has mastered React in the browser already carries the core knowledge for React Native: components, hooks and state work identically. What changes are the building blocks for the interface, the styling model and the way native functionality such as camera or push notifications gets attached. This article shows the direct transition with concrete code.
Table of Contents
- 1. What React Native actually is
- 2. View, Text and Pressable instead of div, span and button
- 3. StyleSheet instead of CSS: understanding the layout model
- 4. Navigation without a browser router
- 5. Hooks and state: what stays the same
- 6. Attaching native modules and platform APIs
- 7. Platform differences between iOS and Android
- 8. Typical beginner mistakes
- 9. React web versus React Native compared
- 10. Summary
- 11. FAQ
1. What React Native actually is
React Native is not a browser wrapped in a shell, it is a framework that translates React components into actual native interface elements on iOS and Android. When a component renders in React Native, a real UIView is created on iOS and a real android.view.View on Android. There is no DOM, no browser renderer and no HTML underneath. This fact is the most important difference to technologies such as Cordova or older WebView based approaches that ultimately display a web page inside a native container.
For web developers, entering React Native mostly means relearning the building blocks, not the programming model. JSX, components, props, state and hooks work identically. The React reconciler follows the same rules, only that at the end of the rendering pipeline there is no DOM tree but a native view tree command sent over the so called bridge or, in newer versions, over the JSI, the JavaScript Interface. Anyone who internalizes this difference immediately understands why some web libraries simply do not work in React Native: anything that directly touches document or window has no counterpart in the native runtime.
The practical entry point is fastest with Expo, a toolchain layer on top of React Native that bundles build configuration, native modules and a development server. For first projects, npx create-expo-app is entirely sufficient. Anyone who wants to dig deeper into native modules later can switch to the bare workflow at any time, more on that in the separate article on Expo versus bare React Native.
2. View, Text and Pressable instead of div, span and button
The first visible difference when switching to React Native is the absence of HTML elements. Instead of <div> you write <View>, instead of <span> or <p> you write <Text>. This rule is strict: plain text outside a Text element causes an error in React Native, while browsers happily allow text nodes directly inside a div. This strictness has a good reason: native text rendering needs explicit control structures for line wrapping, font size and text truncation that the operating system itself handles.
For interactions, Pressable replaces the classic button. Pressable is more flexible than the older TouchableOpacity because its style prop can accept a function that receives the current press state. For lists with many entries, FlatList is mandatory instead of a simple map loop over View elements, because FlatList only renders visible rows and therefore stays performant even with thousands of entries.
import { View, Text, Pressable, FlatList, StyleSheet } from 'react-native';
// React Native core components replace HTML elements directly
function ProductList({ products, onSelect }) {
return (
<FlatList
data={products}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Pressable
onPress={() => onSelect(item)}
style={({ pressed }) => [
styles.row,
pressed && styles.rowPressed,
]}
>
<View style={styles.rowContent}>
<Text style={styles.title}>{item.name}</Text>
<Text style={styles.price}>{item.price.toFixed(2)} EUR</Text>
</View>
</Pressable>
)}
/>
);
}
const styles = StyleSheet.create({
row: { paddingVertical: 12, paddingHorizontal: 16 },
rowPressed: { backgroundColor: '#f1f5f9' },
rowContent: { flexDirection: 'row', justifyContent: 'space-between' },
title: { fontSize: 16, fontWeight: '600', color: '#0f172a' },
price: { fontSize: 16, color: '#0284c7' },
});
3. StyleSheet instead of CSS: understanding the layout model
There is no CSS in React Native, no stylesheets, no selectors, no cascade. Styling happens exclusively through JavaScript objects, usually bundled with StyleSheet.create(). This function validates properties at runtime in development mode and optimizes the objects for production by sending IDs instead of full objects over the bridge. Important for web developers: there is no inheriting of styles through a parent hierarchy. Every component receives its styles explicitly through the style prop, unless one implements inheritance manually through context or prop drilling.
The layout model relies entirely on flexbox, with one decisive difference from the web: the default flexDirection in React Native is column, not row as in the browser. Anyone coming from web development who expects elements side by side must explicitly set flexDirection: 'row'. There is also no grid layout, no CSS variables in the native sense and no media queries. For responsive layouts you use the Dimensions API instead, or the hook useWindowDimensions, which automatically re renders on rotation or split screen changes.
Another difference concerns units: there are no percentage values for font sizes and no rem units. All numeric values in React Native are density independent pixels that the operating system automatically scales to the pixel density of the given device. A fontSize: 16 looks just as large on a high resolution device as it does on an older device with lower pixel density.
import { View, useWindowDimensions, StyleSheet } from 'react-native';
// Responsive layout without media queries
function ResponsiveGrid({ children }) {
const { width } = useWindowDimensions();
const columns = width > 600 ? 3 : 1;
return (
<View style={[styles.container, { flexDirection: 'row', flexWrap: 'wrap' }]}>
{children.map((child, index) => (
<View key={index} style={{ width: `${100 / columns}%`, padding: 8 }}>
{child}
</View>
))}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
});
4. Navigation without a browser router
Since there is no browser, there is also no URL history in the classic sense and no React Router. For navigation in React Native, React Navigation has become the standard library. It maps stack, tab and drawer navigation to native transition animations and manages navigation state as a serializable tree that can even be used for deep linking and state restoration after an app restart.
The conceptual difference from a web router: instead of matching routes through paths, React Native defines screens as named components inside a navigator and navigates programmatically with navigation.navigate('ScreenName', params). Parameters are passed not through the URL but through a typed object, which combines cleanly with TypeScript to guard against typos. Important in practice: every navigator should be defined in its own file, so the navigation structure stays readable as the app grows.
5. Hooks and state: what stays the same
useState, useEffect, useMemo, useCallback, useContext and custom hooks behave in React Native exactly as they do in React for the browser. That is good news for the transition: an existing custom hook for form validation or for data fetching with TanStack Query can usually be reused unchanged, as long as it does not use DOM specific APIs. The context mechanism for global state also works identically, as do state managers such as Zustand or Redux Toolkit.
The difference lies in the side effects themselves. A useEffect that registers a window.addEventListener('resize', …) in the web must be replaced in React Native by Dimensions.addEventListener or by the already mentioned hook useWindowDimensions. Effects that need to react to app lifecycle events, for instance when the app moves to the background, use the AppState API instead of the browser's page visibility API. Anyone who knows this translation table for the most common browser APIs can port existing web logic to React Native in minutes rather than hours.
6. Attaching native modules and platform APIs
The biggest conceptual jump for web developers is access to native functionality such as camera, location, push notifications or biometrics. In React Native, so called native modules handle this task: JavaScript libraries with a native implementation in Swift or Objective-C for iOS and in Kotlin or Java for Android. For the vast majority of use cases you do not need to write these modules yourself, since the Expo ecosystem and community packages such as react-native-permissions already provide finished, tested implementations.
It is important to understand that access rights in React Native must be requested explicitly at the operating system level. Unlike the browser, where a permission API is requested during runtime, native apps must declare certain permissions in advance in a configuration file, in the app.json for Expo, directly in Info.plist for iOS and AndroidManifest.xml for Android in the bare workflow. If this declaration is missing, the operating system rejects the access without comment, which is a common mistake during the first own native module integrations.
import * as Location from 'expo-location';
import { useState, useEffect } from 'react';
// Accessing a native device API through an Expo module
function useCurrentLocation() {
const [coords, setCoords] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let isMounted = true;
async function requestLocation() {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
if (isMounted) setError('Location permission denied');
return;
}
const position = await Location.getCurrentPositionAsync({});
if (isMounted) setCoords(position.coords);
}
requestLocation();
return () => { isMounted = false; };
}, []);
return { coords, error };
}
7. Platform differences between iOS and Android
Despite the promise of writing cross platform code, iOS and Android differ visibly in several places within React Native. Shadows are defined on iOS through the properties shadowColor, shadowOffset and shadowOpacity, while on Android a single property called elevation is used instead. Setting only one of the two variants means the shadow simply does not show up on the other platform. Similar issues apply to status bars, safe areas and keyboard behavior.
For platform specific code, React Native offers two mechanisms: the Platform module with Platform.OS and Platform.select() for inline branching, and file extensions such as Button.ios.js and Button.android.js, between which the bundler automatically chooses based on the target platform. The latter is recommended as soon as platform specific code spans more than a few lines, since it substantially improves readability compared to nested Platform.select() calls buried in layout code.
8. Typical beginner mistakes
The most common beginner mistake is trying to import web libraries directly into React Native that internally access document or localStorage. Such packages break immediately with cryptic runtime errors, since these browser globals simply do not exist in the native environment. The solution is always to specifically look for a library explicitly written for React Native, such as @react-native-async-storage/async-storage as a replacement for localStorage.
A second common mistake is forgetting SafeAreaView, or the more modern react-native-safe-area-context. Without this safeguard, content slides under the status bar or the home indicator bar on devices with a notch or rounded corners. A third mistake is assuming that console.log output is just as performant in production as on the web: in React Native, excessive logging costs measurable time, because every message is sent over the bridge to the native debugger, unless remote debugging over JSI is active.
9. React web versus React Native compared
The following overview summarizes the central differences that most commonly cause confusion when switching from React for the browser to React Native, and directly affect productivity during the onboarding period.
| Area | React Web | React Native | Consequence |
|---|---|---|---|
| Base elements | div, span, button |
View, Text, Pressable |
No HTML, own component set |
| Styling | CSS, selectors, cascade | StyleSheet.create() |
No inheritance, no selectors |
| Default flex | flexDirection: row |
flexDirection: column |
Layouts often thought out differently |
| Navigation | React Router, URL based | React Navigation, screen tree | No path matching |
| Persistence | localStorage |
AsyncStorage |
Asynchronous API instead of synchronous |
Anyone who has internalized these five points has already mastered most of the conceptual transition. The rest is detail work: getting to know platform quirks, exploring the Expo ecosystem, and testing on real devices, since a simulator does not correctly reproduce every behavior, for instance camera or push notifications.
Mironsoft
React and React Native development for web and mobile
Turning your React knowledge into a mobile app?
We support your team through the entry into React Native: from the architecture decision through native module integration to the app store rollout, with a focus on reusing your existing React knowledge.
Architecture consulting
Deciding between Expo and the bare workflow for your specific project
Team onboarding
Training React web developers specifically on React Native patterns
Native modules
Clean integration of camera, push services and biometrics
10. Summary
React Native fundamentals can be summarized in a few core points: the programming model with components, hooks and state stays identical to React in the browser, while the building blocks for the interface and the styling model differ fundamentally. View, Text and Pressable replace HTML elements, StyleSheet.create() replaces CSS, flexbox with column as the default direction instead of row forms the layout foundation. Navigation runs through React Navigation instead of a URL router.
Anyone who has once understood these React Native fundamentals can transfer existing React knowledge to mobile applications in a short time. Starting with Expo minimizes native toolchain complexity at the beginning, while native modules and platform differences only become relevant as the project matures. It remains important to test on real devices, since simulators do not correctly reproduce every native behavior.
React Native Fundamentals for Web Developers, the key points at a glance
Components
View, Text, Pressable and FlatList replace HTML elements. Plain text must always sit inside Text.
Styling
No CSS, no inheritance. StyleSheet.create() with flexbox, default direction is column instead of row.
Programming model
Hooks, props and state work identically to React in the browser. Custom hooks are mostly directly reusable.
Native access
Camera, location and push run through native modules. Permissions must be declared in the app configuration.