Sharing Code Between React Native and Web

Build once, deploy everywhere. Learn how React Native for Web enables code sharing across iOS, Android, and web platforms with practical strategies and best practices.

What is React Native for Web?

React Native for Web is a compatibility layer that translates React Native components into their web equivalents. When you use React Native components like View, Text, and Image in a web environment, React Native for Web automatically transforms them into standard HTML elements--View becomes a div, Text becomes a span or paragraph element, and Image becomes an img tag. This translation happens at runtime, allowing developers to write components once and have them render appropriately across different platforms.

The component mapping approach means your custom UI components created for mobile applications--including buttons, cards, navigation elements, and form controls--can be reused directly in web deployments without modification. Your investment in React Native mobile app development extends naturally to the web, maximizing the value of every line of code your team writes.

The technology builds on React Native's component-based architecture while leveraging the DOM for web rendering. Unlike traditional React web applications that use JSX with HTML elements, React Native for Web maintains the React Native programming model, meaning developers continue using components imported from react-native rather than switching between different component libraries. This consistency allows teams to apply their existing React Native expertise directly to web projects, reducing the learning curve and accelerating delivery timelines.

React Native for Web integrates seamlessly with the broader React Native ecosystem, meaning teams can continue using their existing knowledge of React Native APIs, styling patterns, and component composition strategies. The framework handles the complexity of mapping platform-specific behaviors to their web counterparts, including touch interactions, keyboard events, and accessibility requirements.

The Benefits of Code Sharing

Adopting a unified codebase strategy offers substantial advantages for development teams working across mobile and web platforms.

Reduced Development Time

When features are implemented once and automatically work across platforms, the effort required to ship new functionality drops significantly. A team no longer needs separate specialists for iOS, Android, and web development, and coordination overhead between teams disappears. Features can be tested and refined in one place, ensuring consistency regardless of how users access the application. This efficiency gain compounds over time as your digital product grows and evolves.

Improved Code Quality

Business logic, state management, and data handling exist in a single location. Bug fixes apply universally rather than requiring three separate changes. Unit tests and integration tests cover code paths used by all platforms, increasing confidence in the overall system. When a component is tested on one platform, it works correctly on others unless platform-specific behavior is explicitly introduced.

Lower Maintenance Costs

Security updates, dependency upgrades, and performance optimizations need to be performed once rather than three times. Documentation remains unified, reducing the risk of outdated or contradictory information across platforms. New team members can become productive more quickly because they learn one codebase and one set of patterns rather than three different approaches.

Consistent User Experience

The same UI components work identically across platforms, ensuring users have a coherent experience whether they're using your iOS app, Android application, or web interface. This brand consistency builds trust and recognition, making your digital presence feel unified and professional across every touchpoint.

Key Benefits of React Native for Web

Why leading development teams choose cross-platform code sharing

Zero Configuration

Expo handles all the heavy lifting. Focus on building features instead of fighting build tools.

Maximum Code Reuse

Business logic and UI components work across platforms. Custom components work everywhere.

SEO-Friendly

With App Router, you get proper URLs and SEO-friendly routing that search engines can index.

Faster Iteration

Ship features faster with a shared codebase. Changes are visible across all platforms immediately.

Getting Started with Expo

Expo provides the easiest path to React Native for Web development. With Expo, web support is included out of the box, eliminating the complex configuration that historically accompanied cross-platform development. Creating a new project takes just a few commands, and running the development server automatically detects when you want to view the web build.

To create a new Expo project optimized for web and mobile sharing, run the following commands:

npx create-expo-app my-app
cd my-app
npx expo start

Once the server is running, pressing "w" in the terminal opens the application in a web browser. This seamless transition between platforms exemplifies Expo's philosophy of reducing friction in the development process. Any changes made to the code are reflected immediately in both mobile and web builds, enabling rapid iteration and testing across all target platforms.

Expo App Router for Web Navigation

Expo's App Router brings filesystem-based routing to React Native, working similarly to Next.js for web applications. Routes are defined by the folder structure within the app directory, eliminating the need for manual route configuration. This approach provides proper URL handling and SEO-friendly navigation essential for web applications.

A typical project structure follows this pattern:

app/
├── index.tsx
├── about/
│ ├── index.tsx
│ └── _layout.tsx

Navigation between pages uses the Link component from expo-router:

import { View, Text, Pressable } from 'react-native';
import { Link } from 'expo-router';

export default function Home() {
 return (
 <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
 <Text>Welcome to React Native Web with Expo!</Text>
 <Link href="/about">
 <Pressable>
 <Text style={{ color: 'blue', marginTop: 20 }}>Go to About Page</Text>
 </Pressable>
 </Link>
 </View>
 );
}

This pattern ensures consistent navigation behavior across platforms while maintaining proper URL structures for web crawlers and users who share links. The filesystem-based routing aligns with web standards, making your React Native web application feel native to both users and search engines.

Platform-Specific Code Patterns

While maximizing code sharing is the primary goal, certain scenarios require platform-specific implementations. React Native provides the Platform module for detecting the current operating system and executing conditional logic.

The Platform module provides several useful APIs for platform detection:

import { Platform, View, Text, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
 container: {
 flex: 1,
 padding: Platform.OS === 'web' ? 20 : 16,
 backgroundColor: Platform.OS === 'ios' ? '#f5f5f5' : '#ffffff',
 },
});

export default function AdaptiveComponent() {
 return (
 <View style={styles.container}>
 <Text>
 {Platform.OS === 'web'
 ? 'Running on web browser'
 : `Running on ${Platform.OS}`
 }
 </Text>
 </View>
 );
}

For more complex platform-specific behavior, the Platform.select method provides a cleaner syntax that returns different values based on the current platform:

import { Platform, View, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
 container: Platform.select({
 ios: { paddingTop: 44 },
 android: { paddingTop: 24 },
 web: { paddingTop: 0 },
 }),
});

Best practices for minimizing platform-specific code include isolating it in dedicated adapter layers, using clear naming conventions for platform-specific files (like Component.web.tsx), and documenting why each platform divergence is necessary. When platform-specific code is unavoidable, React Native's module resolution automatically picks the correct file based on the current platform, keeping your shared code clean and maintainable.

Performance Considerations

React Native for Web delivers good performance for most use cases, though it's important to understand the trade-offs involved. The framework compiles to standard DOM elements, meaning web applications built with React Native for Web perform comparably to traditional React applications for similar functionality. For dashboards, admin panels, e-commerce interfaces, and content-driven applications, the performance is more than sufficient.

However, performance characteristics differ from pure web frameworks in several ways. React Native for Web introduces an additional abstraction layer that translates component updates to DOM manipulations. In most cases, this overhead is imperceptible, but highly interactive applications with frequent state changes might experience minor differences compared to hand-optimized React code. For applications where every millisecond matters--such as real-time gaming or complex data visualization--pure web technologies may still offer advantages.

Best Practices for Performance

  • Use FlatList with appropriate virtualization settings for long lists, ensuring the windowSize and initialNumToRender props are tuned for your use case

  • Memoize expensive computations with useMemo and useCallback to prevent unnecessary recalculations that trigger re-renders

  • Lazy load routes using React.lazy and Suspense to reduce initial bundle size and improve time-to-interactive

  • Optimize images with appropriate sizing and format selection, leveraging the Image component's resizeMode prop for optimal display

  • Test performance early using browser developer tools and React DevTools to identify bottlenecks before they become systemic issues across your cross-platform application

Performance testing should be conducted on actual devices and browsers rather than relying solely on development environment benchmarks. Real-world conditions--network latency, device variability, and user environment differences--can significantly impact perceived performance.

Handling Lists with FlatList
1import React from 'react';2import { FlatList, Text, View, StyleSheet } from 'react-native';3 4const DATA = [5 { id: '1', title: 'First item' },6 { id: '2', title: 'Second item' },7 { id: '3', title: 'Third item' },8];9 10const renderItem = ({ item }: { item: typeof DATA[0] }) => (11 <View style={styles.listItem}>12 <Text>{item.title}</Text>13 </View>14);15 16export default function MyList() {17 return (18 <FlatList19 data={DATA}20 renderItem={renderItem}21 keyExtractor={item => item.id}22 contentContainerStyle={styles.listContent}23 />24 );25}26 27const styles = StyleSheet.create({28 listItem: {29 padding: 10,30 borderBottomWidth: 1,31 borderColor: '#ccc',32 },33 listContent: {34 paddingBottom: 20,35 },36});

Managing API Calls

Data fetching works consistently across platforms using standard React hooks. The same patterns used in React web applications apply directly to React Native for Web, ensuring your API integrations work seamlessly everywhere:

API Calls in React Native for Web
1import React, { useEffect, useState } from 'react';2import { View, Text, ActivityIndicator, FlatList, StyleSheet } from 'react-native';3 4interface DataItem {5 id: number;6 title: string;7}8 9export default function DataFetcher() {10 const [data, setData] = useState<DataItem[]>([]);11 const [loading, setLoading] = useState(true);12 13 useEffect(() => {14 async function fetchData() {15 try {16 const response = await fetch('https://api.example.com/data');17 const json = await response.json();18 setData(json);19 } catch (error) {20 console.error('Error fetching data:', error);21 } finally {22 setLoading(false);23 }24 }25 26 fetchData();27 }, []);28 29 if (loading) {30 return (31 <View style={styles.centered}>32 <ActivityIndicator size="large" />33 </View>34 );35 }36 37 return (38 <FlatList39 data={data}40 keyExtractor={item => item.id.toString()}41 renderItem={({ item }) => (42 <View style={styles.item}>43 <Text>{item.title}</Text>44 </View>45 )}46 />47 );48}49 50const styles = StyleSheet.create({51 centered: {52 flex: 1,53 justifyContent: 'center',54 alignItems: 'center',55 },56 item: {57 padding: 16,58 borderBottomWidth: 1,59 borderColor: '#eee',60 },61});

Best Practices for Maximum Reusability

Successful code sharing requires thoughtful architecture from the project's inception. Several practices help maximize reusability while minimizing the need for platform-specific modifications:

Design Platform-Agnostic Components First

Create components that express their functionality through React Native primitives without assuming platform-specific behaviors. Avoid relying on native-specific APIs unless absolutely necessary, and when they are required, abstract them behind interfaces that can be implemented differently per platform.

Use StyleSheet for Consistent Styling

StyleSheet.create provides CSS-in-JS functionality that compiles appropriately on each platform. This approach ensures consistent styling behavior and enables optimizations like style flattening that improve performance. Avoid inline style objects except for dynamic values that change at runtime.

Separate Logic from Presentation

Business logic, data transformations, and state management should exist in pure JavaScript modules that have no dependency on React Native or platform-specific imports. These modules can be tested independently and reused across any JavaScript environment, whether in your mobile app, web application, or backend services.

Leverage TypeScript for Cross-Platform Safety

TypeScript's type system catches errors that might only manifest on specific platforms. Define interfaces for component props and API responses, and use generics appropriately to ensure type safety across all code paths.

Test on All Target Platforms Early

Don't wait until the end of development to test web builds. Regularly run your application in web mode to catch issues before they accumulate. The Expo development server makes this seamless by supporting simultaneous connections from multiple platform targets.

Maintain Clear Platform Boundaries

When platform-specific code is unavoidable, isolate it in dedicated files or modules with clear naming conventions (e.g., MyComponent.web.tsx, MyComponent.native.tsx). React Native's module resolution automatically picks the correct file based on the current platform.

Common Challenges and Solutions

React Native for Web development presents several challenges that teams should anticipate and prepare for:

Touch Behavior Differences

Web browsers handle touch events differently than mobile devices, particularly with regard to hover states, scroll behaviors, and gesture recognition. The Pressable component provides consistent press detection across platforms, but complex gestures may require additional handling. For web-specific interactions, consider using react-native-gesture-handler with appropriate web configurations.

CSS Layout Quirks

React Native's Flexbox implementation differs slightly from web CSS in subtle ways--default flex direction is column rather than row, and certain properties like percentage-based dimensions behave differently. Thorough testing across browsers reveals these differences early, and creating custom components that normalize behavior across platforms prevents scattered fixes throughout your codebase.

Missing Native Modules

Some React Native packages that work on iOS and Android lack web implementations. Before selecting third-party libraries, verify web compatibility by checking package documentation, GitHub issues, and testing early in development. Popular packages like those for analytics, crash reporting, and storage often have official or community-maintained web shims.

SEO Considerations

While React Native for Web supports proper routing and can be configured for SEO, achieving optimal search engine visibility requires additional attention. Ensure server-side rendering or static generation is configured if deep indexing is critical. Use proper meta tags, semantic HTML, and structured data markup to maximize discoverability for your web presence.

Browser Compatibility

Web applications must function across multiple browsers with varying capability levels. The targeting strategy should align with your user base--supporting modern evergreen browsers simplifies development while maintaining wider compatibility requires additional testing and potentially fallback implementations for cutting-edge features.

Ready to Build Cross-Platform Applications?

Our team specializes in React Native development with web capabilities. Let's discuss how we can help you reach users across all platforms with a single codebase.

Frequently Asked Questions

Sources

  1. LogRocket: Sharing code with React Native for Web - Comprehensive tutorial covering component mapping and practical code sharing strategies
  2. LogicRays: React Native For Web Guide - Detailed cross-platform development guide with performance considerations
  3. DEV Community: From Mobile to Browser - A Guide to React Native for Web - Practical developer guide with Expo App Router and code examples
  4. React Native Official Documentation - Platform-Specific Code - Official guidance on platform detection and implementation patterns