React Native Fundamentals for Web Developers
AI generated
</>
{ }
React Native · Mobile Development · Cross Platform
React Native Fundamentals for Web Developers
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.

18 min read Components · Styling · Navigation · Native Modules React Native 0.76+ · iOS · Android

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 },
});

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.

11. FAQ: React Native Fundamentals for Web Developers

1Do I need Swift or Kotlin?
No, JavaScript or TypeScript is enough for most apps. Native languages are only needed for custom native modules.
2Can I reuse existing React components?
Pure logic hooks usually yes. Components with HTML elements or CSS classes need to be rewritten.
3Why is flexDirection different?
Default is column instead of row, matched to typical vertical mobile layouts.
4How does styling work without CSS?
Through StyleSheet.create() with JavaScript objects, no cascade, no automatic inheritance.
5What replaces localStorage?
The asynchronous AsyncStorage API from the matching package, every access runs through promises.
6Do I need a Mac?
Only mandatory for iOS builds. Android works on any operating system, iOS can also be tested with Expo through a cloud build.
7How do I navigate between screens?
With React Navigation through navigation.navigate(), screens are components in a navigator tree.
8Why do some npm packages not work?
They access document or window, which do not exist in the native runtime.
9What is SafeAreaView?
Prevents content from sliding under the notch or home indicator, essential for modern devices.
10How do I test camera or push?
Reliably only on real devices, simulators often reproduce these functions incompletely.