React vs React Native Development Practices: Key Architectural Differences Explained
React and React Native share identical component models and hook APIs, but diverge fundamentally in renderer architecture, styling implementation, and platform API access that dictate daily development workflows.
When building applications with the React library from the facebook/react repository, developers must choose between targeting web browsers or native mobile platforms. While both environments leverage the same core reconciler and component lifecycle, the development practices differ significantly in how UI primitives are rendered, how styles are applied, and how platform-specific capabilities are accessed.
Renderer Architecture: DOM vs Native UI Primitives
The most fundamental difference between React web and React Native development lies in the renderer implementation that translates component trees into actual platform UI.
Web Rendering with react-dom
React for web utilizes the react-dom package to target the browser's DOM API. In packages/react-dom/README.md, the renderer exposes the modern createRoot API that mounts components to real DOM nodes using standard browser operations like appendChild and removeChild.
The renderer relies entirely on the browser's layout engine and CSS styling capabilities. When you render a <div> or <span>, React creates actual HTML elements that the browser parses, styles, and paints.
Native Rendering with react-native-renderer
React Native employs a separate renderer located in packages/react-native-renderer/src/ReactNativeRenderer.js (legacy) and packages/react-native-renderer/src/ReactFabric.js (modern Fabric architecture). Instead of creating DOM nodes, this renderer constructs a shadow tree of native UI primitives.
The shadow tree is translated into actual native view hierarchies—iOS UIView or Android View objects—through the Fabric renderer or legacy bridge. This means when you write <View> or <Text> in React Native, you are not creating HTML elements but rather instructing the native platform to instantiate platform-specific UI components.
Styling and Layout Practices
The divergence in renderer architecture forces distinct styling paradigms that fundamentally change how developers write and organize presentation logic.
CSS and Class Names vs StyleSheet API
React web development uses standard CSS methodologies. Developers apply styles via className attributes referencing CSS files, CSS Modules, or CSS-in-JS libraries. The browser's full CSS cascade, media queries, and selector specificity govern the final appearance.
React Native replaces CSS with the JavaScript-only StyleSheet API. As noted in the repository's documentation referencing "Learn Once, Write Anywhere," all styling occurs through JavaScript objects that are compiled into native style dictionaries. There are no CSS files, no class names, and no cascade—only flat style objects applied directly to components via the style prop.
Flexbox Implementation Differences
Both platforms use Flexbox for layout, but React Native's implementation differs from web Flexbox in several key ways:
- Default direction: React Native defaults to
columnrather thanrow - Unitless values: Dimensions are specified as unitless numbers representing density-independent pixels (dp) rather than
px,em, orrem - No percentage support: Legacy React Native versions lack percentage-based dimensions (though modern versions support them with constraints)
Platform-Specific APIs and Development Workflow
The host environment differences necessitate distinct tooling, debugging strategies, and platform API access patterns.
Browser Environment vs Native Modules
React web applications run in the browser sandbox with direct access to Web APIs: window, document, fetch, localStorage, and DOM events. Third-party libraries typically wrap or polyfill these standard interfaces.
React Native provides access to native device capabilities through native modules exposed via the bridge or Fabric. APIs like AsyncStorage, Geolocation, and CameraRoll are not JavaScript implementations but rather thin wrappers that serialize calls to native code. This requires understanding platform-specific linking (or autolinking in modern versions) and sometimes writing native code in Objective-C, Swift, Java, or Kotlin.
Build Tools and Debugging
The development workflow diverges significantly in tooling:
- Bundling: Web projects typically use Webpack, Vite, or Parcel. React Native uses Metro, a custom bundler designed for native platforms that handles platform-specific file extensions (
.ios.js,.android.js) and asset scaling. - Debugging: Both platforms support React DevTools, as documented in
packages/react-devtools/README.md. However, the backend implementation differs—web uses the DOM renderer backend while native requires the standalone DevTools UI or in-app inspector. For native, you must also use platform-specific debuggers (Xcode Instruments, Android Studio Profiler) to analyze native thread performance. - Testing: Web applications use Jest with DOM testing utilities (
@testing-library/react). React Native requires@testing-library/react-nativebecause the underlying renderer is not a DOM implementation, as noted inpackages/react-test-renderer/README.md.
Code Comparison: Counter Component
The following examples demonstrate how the same logical component differs in implementation between web and native platforms.
React Web Implementation
Using the modern createRoot API from packages/react-dom:
import { createRoot } from 'react-dom/client';
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Clicked {count} times
</button>
);
}
const root = createRoot(document.getElementById('root')!);
root.render(<Counter />);
React Native Implementation
Using native primitives and AppRegistry from the native renderer:
import { AppRegistry, Text, TouchableOpacity, View } from 'react-native';
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<TouchableOpacity onPress={() => setCount(c => c + 1)}>
<Text>Clicked {count} times</Text>
</TouchableOpacity>
</View>
);
}
AppRegistry.registerComponent('MyApp', () => Counter);
Styling Syntax Comparison
Web development uses CSS Modules or standard CSS:
/* Web – CSS module */
import styles from './Button.module.css';
<button className={styles.primary}>Web Button</button>
React Native uses the StyleSheet API with JavaScript objects:
/* React Native – StyleSheet */
import { StyleSheet, Text, TouchableOpacity } from 'react-native';
const styles = StyleSheet.create({
primary: { backgroundColor: '#0066ff', padding: 12, borderRadius: 4 },
});
<TouchableOpacity style={styles.primary}>
<Text style={{ color: '#fff' }}>Native Button</Text>
</TouchableOpacity>
Summary
-
Renderer architecture determines the fundamental difference: React uses
react-domto manipulate browser DOM nodes, while React Native usesreact-native-renderer(includingReactFabric.jsfor the modern architecture) to construct native platform view hierarchies via shadow trees. -
Styling approaches are incompatible between platforms: web relies on CSS cascade and class names, while React Native requires JavaScript-only
StyleSheetobjects with Flexbox layout and density-independent units. -
Platform APIs differ significantly: web applications access browser APIs like
windowandlocalStorage, while React Native bridges to native modules for device capabilities and requires platform-specific build tools like Metro and Xcode/Android Studio. -
Development workflows diverge in debugging and testing: both support React DevTools, but native development requires additional platform-specific profilers and
@testing-library/react-nativerather than DOM-based testing utilities.
Frequently Asked Questions
What is the main technical difference between React and React Native renderers?
The main technical difference lies in the host environment target. According to the facebook/react source code, React for web uses the react-dom package to mutate actual browser DOM nodes using standard web APIs like appendChild. In contrast, React Native uses the react-native-renderer package (specifically implementations in ReactNativeRenderer.js and ReactFabric.js) to create a shadow tree that is translated into platform-native view objects (iOS UIView or Android View) rather than HTML elements.
Can I use CSS files in React Native development?
No, React Native does not support traditional CSS files or the CSS cascade. As implemented in the React Native architecture, styling must be done through JavaScript objects using the StyleSheet API. While the layout engine uses Flexbox algorithms similar to the web, styles are defined as JavaScript objects with camelCase properties (e.g., backgroundColor instead of background-color) and applied directly to components via the style prop, bypassing the browser's CSS parsing entirely.
How does debugging differ between React web and React Native applications?
Both platforms support React DevTools, as documented in packages/react-devtools/README.md, but the debugging workflow differs significantly. For React web applications, developers primarily use browser DevTools (Chrome DevTools, Firefox Developer Tools) to inspect the DOM, monitor network requests, and profile performance. For React Native, developers must use the standalone React DevTools UI or in-app inspector for the component tree, while native performance profiling requires platform-specific tools like Xcode Instruments for iOS or Android Studio Profiler for Android, since the app runs actual native code rather than JavaScript in a browser context.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →