React Native Web enables you to run React Native components and APIs on the web, creating truly universal applications. This comprehensive guide covers everything from setup to deployment, helping you build performant cross-platform apps that work seamlessly across mobile and web.
1. Understanding React Native Web
What is React Native Web?
React Native Web is an accessible implementation of React Native's Components and APIs that is interoperable with React DOM. It allows you to run React Native code on the web without modification, enabling true code sharing across iOS, Android, and web platforms.
- 🌐 Single codebase for mobile and web
- ♿ Built-in accessibility features
- 📱 Responsive design support
- 🎨 Consistent styling across platforms
- 🔧 Incremental adoption possible
Major companies like Facebook, Twitter, and Flipkart use React Native Web in production, proving its reliability and scalability.
React Native Web vs Traditional Web Development
Here's how React Native Web compares to traditional web development approaches:
Aspect | React Native Web | Traditional React |
---|---|---|
Code Sharing | 95%+ across platforms | Web-only, separate mobile apps |
Component Library | Unified React Native components | Separate web component libraries |
Styling | StyleSheet API (CSS-in-JS) | CSS, styled-components, etc. |
Bundle Size | ~42KB gzipped base | Varies by setup |
Learning Curve | React Native knowledge required | Web-specific knowledge |
2. Setting Up React Native Web
New Project Setup
The easiest way to start with React Native Web is using Expo, which has built-in web support:
# Create new Expo project with web support
npx create-expo-app MyUniversalApp --template
# Navigate to project
cd MyUniversalApp
# Install web dependencies
npx expo install react-dom react-native-web @expo/webpack-config
# Start development server
npx expo start --web
For vanilla React Native projects, you can add web support manually:
# Install React Native Web
npm install react-native-web
# Install dev dependencies
npm install --save-dev @babel/preset-env @babel/preset-react
npm install --save-dev webpack webpack-cli webpack-dev-server
npm install --save-dev babel-loader html-webpack-plugin
Webpack Configuration
Configure Webpack to properly resolve React Native Web modules:
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './index.web.js',
mode: 'development',
module: {
rules: [
{
test: /.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
'module:metro-react-native-babel-preset'
]
}
}
}
]
},
resolve: {
alias: {
'react-native$': 'react-native-web',
'react-native-svg': 'react-native-svg-web'
},
extensions: ['.web.js', '.js', '.jsx', '.ts', '.tsx']
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
devServer: {
historyApiFallback: true,
port: 3000
}
};
Entry Point Setup
Create a web-specific entry point for your application:
// index.web.js
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import App from './App';
// Register the app for web
AppRegistry.registerComponent(appName, () => App);
AppRegistry.runApplication(appName, {
initialProps: {},
rootTag: document.getElementById('app-root')
});
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Native Web App</title>
<style>
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
#app-root {
display: flex;
flex: 1;
height: 100vh;
}
</style>
</head>
<body>
<div id="app-root"></div>
</body>
</html>
3. Core Components and APIs
Universal Components
React Native Web provides web implementations for all core React Native components:
import React from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
TextInput,
Image
} from 'react-native';
function UniversalComponent() {
return (
<View style={styles.container}>
<ScrollView style={styles.scrollView}>
<Text style={styles.title}>Universal App</Text>
<Image
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
style={styles.image}
accessibilityLabel="React Native Logo"
/>
<TextInput
style={styles.input}
placeholder="Enter text here"
accessibilityLabel="Text input field"
/>
<TouchableOpacity
style={styles.button}
onPress={() => alert('Button pressed!')}
accessibilityRole="button"
>
<Text style={styles.buttonText}>Press Me</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
scrollView: {
flex: 1,
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
color: '#333',
},
image: {
width: 100,
height: 100,
alignSelf: 'center',
marginBottom: 20,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
marginBottom: 20,
backgroundColor: 'white',
},
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
});
export default UniversalComponent;
Platform-Specific Code
Use platform extensions or Platform API to write platform-specific code:
import { Platform } from 'react-native';
// Method 1: Platform API
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Platform.select({
ios: 20,
android: 25,
web: 0,
default: 0
}),
},
text: {
...Platform.select({
web: {
cursor: 'pointer',
userSelect: 'none',
},
default: {},
}),
}
});
// Method 2: Platform-specific files
// Component.ios.js
// Component.android.js
// Component.web.js
// Component.js (fallback)
// Method 3: Conditional rendering
function CrossPlatformComponent() {
return (
<View>
{Platform.OS === 'web' ? (
<div style={{ cursor: 'pointer' }}>Web-specific content</div>
) : (
<Text>Mobile content</Text>
)}
</View>
);
}
Web-Specific Features
Leverage web-specific APIs and features when needed:
import React, { useEffect } from 'react';
import { Platform } from 'react-native';
function WebSpecificFeatures() {
useEffect(() => {
if (Platform.OS === 'web') {
// Set document title
document.title = 'My Universal App';
// Add meta tags
const metaDescription = document.createElement('meta');
metaDescription.name = 'description';
metaDescription.content = 'A universal React Native app';
document.head.appendChild(metaDescription);
// Handle keyboard events
const handleKeyPress = (event) => {
if (event.key === 'Escape') {
// Handle escape key
}
};
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}
}, []);
return null;
}
4. Styling Best Practices
Responsive Design
Create responsive layouts using Flexbox and dimension-based styling:
import React from 'react';
import { View, Text, StyleSheet, useWindowDimensions } from 'react-native';
function ResponsiveLayout() {
const { width, height } = useWindowDimensions();
const isDesktop = width >= 1024;
const isTablet = width >= 768 && width < 1024;
const isMobile = width < 768;
return (
<View style={styles.container}>
<View style={[
styles.content,
isDesktop && styles.desktopContent,
isTablet && styles.tabletContent,
isMobile && styles.mobileContent
]}>
<Text style={styles.title}>
Responsive Layout ({width}x{height})
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
content: {
flex: 1,
padding: 20,
alignSelf: 'center',
},
desktopContent: {
maxWidth: 1200,
flexDirection: 'row',
flexWrap: 'wrap',
},
tabletContent: {
maxWidth: 768,
paddingHorizontal: 40,
},
mobileContent: {
width: '100%',
paddingHorizontal: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
});
CSS-in-JS Best Practices
Follow React Native Web styling conventions for optimal performance:
import { StyleSheet } from 'react-native';
// ✅ Good: Use StyleSheet.create for performance
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffffff',
},
// Use object shorthand for better readability
centerContent: {
justifyContent: 'center',
alignItems: 'center',
},
// Group related styles
text: {
fontSize: 16,
lineHeight: 24,
color: '#333333',
},
boldText: {
fontWeight: 'bold',
},
});
// ✅ Good: Combine styles efficiently
<Text style={[styles.text, styles.boldText]}>
Bold Text
</Text>
// ❌ Avoid: Inline styles for static values
<View style={{ flex: 1, backgroundColor: '#ffffff' }}>
// ✅ Better: Use StyleSheet for static styles
<View style={styles.container}>
// ✅ Good: Use inline styles for dynamic values only
<View style={[styles.container, { opacity: isVisible ? 1 : 0 }]}>
// Web-specific styles using Platform
const webStyles = Platform.OS === 'web' ? {
cursor: 'pointer',
transition: 'all 0.2s ease',
':hover': {
backgroundColor: '#f0f0f0',
}
} : {};
Typography and Fonts
Handle fonts consistently across platforms:
// Custom font setup
const typography = StyleSheet.create({
heading1: {
fontSize: 32,
fontWeight: 'bold',
lineHeight: 40,
fontFamily: Platform.select({
ios: 'San Francisco',
android: 'Roboto',
web: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
}),
},
body: {
fontSize: 16,
lineHeight: 24,
fontFamily: Platform.select({
ios: 'San Francisco',
android: 'Roboto',
web: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
}),
},
monospace: {
fontFamily: Platform.select({
ios: 'SF Mono',
android: 'monospace',
web: 'Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
}),
},
});
// Usage
function TypographyExample() {
return (
<View>
<Text style={typography.heading1}>Main Heading</Text>
<Text style={typography.body}>Body text content</Text>
<Text style={typography.monospace}>console.log('Code example');</Text>
</View>
);
}
5. Performance Optimization
Bundle Size Optimization
Optimize your React Native Web bundle for better loading performance:
// webpack.config.js - Production optimizations
const path = require('path');
module.exports = {
// ... other config
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
react: {
test: /[\/]node_modules[\/](react|react-dom)[\/]/,
name: 'react',
chunks: 'all',
}
}
}
},
resolve: {
alias: {
'react-native$': 'react-native-web',
// Tree shake unused modules
'react-native-vector-icons$': 'react-native-vector-icons/dist/FontAwesome',
},
// Prioritize web extensions
extensions: ['.web.js', '.web.ts', '.web.tsx', '.js', '.ts', '.tsx']
}
};
// Use dynamic imports for code splitting
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
Component Performance
Optimize components for smooth web performance:
import React, { memo, useMemo, useCallback } from 'react';
import { FlatList, View, Text } from 'react-native';
// Memoize expensive list items
const ListItem = memo(function ListItem({ item, onPress }) {
const handlePress = useCallback(() => {
onPress(item.id);
}, [item.id, onPress]);
return (
<TouchableOpacity onPress={handlePress}>
<Text>{item.title}</Text>
</TouchableOpacity>
);
});
// Optimize FlatList for web
function OptimizedList({ data }) {
const renderItem = useCallback(({ item }) => (
<ListItem item={item} onPress={handleItemPress} />
), []);
const keyExtractor = useCallback((item) => item.id, []);
const getItemLayout = useCallback((data, index) => ({
length: 60,
offset: 60 * index,
index,
}), []);
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
windowSize={10}
initialNumToRender={20}
/>
);
}
Image Optimization
Handle images efficiently across platforms:
import React from 'react';
import { Image, Platform } from 'react-native';
function OptimizedImage({ src, width, height, ...props }) {
// Use different image sources for web
const imageSource = Platform.OS === 'web'
? {
uri: src,
// Use responsive images on web
...(width && height && {
width,
height,
})
}
: { uri: src };
return (
<Image
source={imageSource}
style={[
{ width, height },
Platform.OS === 'web' && {
objectFit: 'cover',
maxWidth: '100%',
height: 'auto',
}
]}
// Web-specific props
{...(Platform.OS === 'web' && {
loading: 'lazy',
decoding: 'async',
})}
{...props}
/>
);
}
// Usage with responsive images
function ResponsiveImage() {
return (
<OptimizedImage
src="https://example.com/image.jpg"
width={300}
height={200}
alt="Description for accessibility"
/>
);
}
6. Navigation and Routing
React Navigation Web Support
React Navigation works seamlessly with React Native Web:
# Install React Navigation for web
npm install @react-navigation/native @react-navigation/web
npm install react-native-screens react-native-safe-area-context
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { Platform } from 'react-native';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer
// Web-specific linking configuration
linking={{
prefixes: ['https://myapp.com', 'myapp://'],
config: {
screens: {
Home: '/',
Profile: '/profile/:userId',
Settings: '/settings',
},
},
}}
// Fallback for web
fallback={<div>Loading...</div>}
>
<Stack.Navigator
screenOptions={{
// Web-specific header styling
...(Platform.OS === 'web' && {
headerStyle: {
backgroundColor: '#f8f9fa',
}
})
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Deep Linking and URLs
Handle URLs and deep linking properly on web:
import { useLinking } from '@react-navigation/native';
import { Platform } from 'react-native';
function useWebLinking() {
const { getStateFromPath, getPathFromState } = useLinking();
// Custom URL handling for web
React.useEffect(() => {
if (Platform.OS === 'web') {
// Handle browser back/forward buttons
const handlePopState = (event) => {
// Handle state changes
};
window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState);
}
}, []);
return {
getStateFromPath,
getPathFromState,
};
}
// SEO-friendly navigation
function SEOFriendlyScreen({ route }) {
React.useEffect(() => {
if (Platform.OS === 'web') {
// Update document title
document.title = `${route.name} - My App`;
// Update meta description
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.content = `Content for ${route.name}`;
}
}
}, [route.name]);
return (
// Component content
);
}
7. Testing Strategies
Unit Testing
Test your universal components across platforms:
# Install testing dependencies
npm install --save-dev @testing-library/react-native
npm install --save-dev @testing-library/jest-dom
npm install --save-dev jest-environment-jsdom
// __tests__/UniversalComponent.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import UniversalComponent from '../UniversalComponent';
// Mock Platform for testing
jest.mock('react-native/Libraries/Utilities/Platform', () => ({
OS: 'web',
select: (platforms) => platforms.web || platforms.default,
}));
describe('UniversalComponent', () => {
it('renders correctly on web', () => {
const { getByText, getByRole } = render(<UniversalComponent />);
expect(getByText('Universal App')).toBeTruthy();
expect(getByRole('button', { name: 'Press Me' })).toBeTruthy();
});
it('handles button press', () => {
const mockAlert = jest.spyOn(window, 'alert').mockImplementation();
const { getByRole } = render(<UniversalComponent />);
fireEvent.press(getByRole('button', { name: 'Press Me' }));
expect(mockAlert).toHaveBeenCalledWith('Button pressed!');
mockAlert.mockRestore();
});
it('applies correct styles for web platform', () => {
const { getByTestId } = render(<UniversalComponent />);
const button = getByTestId('press-button');
expect(button).toHaveStyle({
cursor: 'pointer',
});
});
});
End-to-End Testing
Use Playwright or Cypress for web-specific E2E testing:
// e2e/web.spec.js (Playwright)
import { test, expect } from '@playwright/test';
test.describe('React Native Web App', () => {
test('should navigate and interact correctly', async ({ page }) => {
await page.goto('http://localhost:3000');
// Test responsive design
await page.setViewportSize({ width: 1200, height: 800 });
await expect(page.locator('[data-testid="desktop-layout"]')).toBeVisible();
await page.setViewportSize({ width: 375, height: 667 });
await expect(page.locator('[data-testid="mobile-layout"]')).toBeVisible();
// Test navigation
await page.click('text=Profile');
await expect(page).toHaveURL(/.*profile/);
// Test form interactions
await page.fill('[placeholder="Enter text here"]', 'Test input');
await page.click('text=Submit');
// Test accessibility
await expect(page.locator('button')).toHaveAttribute('aria-label');
});
test('should work with keyboard navigation', async ({ page }) => {
await page.goto('http://localhost:3000');
// Test keyboard navigation
await page.keyboard.press('Tab');
await page.keyboard.press('Enter');
// Test keyboard shortcuts
await page.keyboard.press('Escape');
});
});
8. Deployment and Production
Build Configuration
Configure your build process for production deployment:
// webpack.prod.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
mode: 'production',
entry: './index.web.js',
output: {
path: path.resolve(__dirname, 'web-build'),
filename: 'static/js/[name].[contenthash:8].js',
chunkFilename: 'static/js/[name].[contenthash:8].chunk.js',
publicPath: '/',
},
optimization: {
minimize: true,
splitChunks: {
chunks: 'all',
name: false,
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Analyze bundle size
process.env.ANALYZE && new BundleAnalyzerPlugin(),
].filter(Boolean),
};
Static Site Generation
Generate static sites for better SEO and performance:
// scripts/static-export.js
const { renderToStaticMarkup } = require('react-dom/server');
const { AppRegistry } = require('react-native-web');
const fs = require('fs');
const path = require('path');
// Import your root component
const App = require('../App').default;
// Register component
AppRegistry.registerComponent('App', () => App);
// Get the component
const { element, getStyleElement } = AppRegistry.getApplication('App');
// Render to static HTML
const html = renderToStaticMarkup(element);
const css = renderToStaticMarkup(getStyleElement());
// Generate HTML template
const template = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Native Web App</title>
${css}
</head>
<body>
<div id="root">${html}</div>
<script src="/bundle.js"></script>
</body>
</html>
`;
// Write to file
fs.writeFileSync(path.join(__dirname, '../web-build/index.html'), template);
console.log('Static HTML generated successfully!');
Progressive Web App (PWA)
Convert your React Native Web app into a PWA:
// public/manifest.json
{
"name": "My Universal App",
"short_name": "UniversalApp",
"description": "A universal React Native app",
"start_url": "/",
"display": "standalone",
"theme_color": "#007AFF",
"background_color": "#ffffff",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
// public/sw.js - Service Worker
const CACHE_NAME = 'universal-app-v1';
const urlsToCache = [
'/',
'/static/js/bundle.js',
'/static/css/main.css',
'/icon-192x192.png'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
// Return cached version or fetch from network
return response || fetch(event.request);
})
);
});
// Register service worker in your app
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('SW registered: ', registration);
})
.catch((registrationError) => {
console.log('SW registration failed: ', registrationError);
});
});
}
9. Common Challenges and Solutions
Library Compatibility
Handle React Native libraries that don't support web:
// utils/platformModules.js
import { Platform } from 'react-native';
// Conditional imports based on platform
export const getLocationModule = () => {
if (Platform.OS === 'web') {
// Use browser geolocation API
return {
getCurrentPosition: (success, error, options) => {
navigator.geolocation.getCurrentPosition(success, error, options);
}
};
} else {
// Use react-native-geolocation-service for mobile
return require('react-native-geolocation-service').default;
}
};
// Mock native modules for web
export const mockNativeModule = (moduleName) => {
if (Platform.OS === 'web') {
console.warn(`${moduleName} is not available on web`);
return {
// Provide web alternatives or no-ops
};
}
return require(moduleName);
};
// Platform-specific component imports
export const SafeAreaView = Platform.OS === 'web'
? require('react-native').View
: require('react-native-safe-area-context').SafeAreaView;
Performance Issues
Address common performance bottlenecks:
import React, { useMemo, useCallback } from 'react';
import { Platform, FlatList } from 'react-native';
// Optimize large lists for web
function OptimizedFlatList({ data, ...props }) {
const webOptimizations = useMemo(() => {
if (Platform.OS === 'web') {
return {
// Use virtual scrolling for large datasets
removeClippedSubviews: true,
maxToRenderPerBatch: 50,
windowSize: 10,
// Reduce re-renders
getItemLayout: props.getItemLayout || ((data, index) => ({
length: 50, // Estimated item height
offset: 50 * index,
index,
})),
};
}
return {};
}, [props.getItemLayout]);
return (
<FlatList
{...props}
{...webOptimizations}
data={data}
/>
);
}
// Debounce search for better performance
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = React.useState(value);
React.useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
function SearchComponent() {
const [searchTerm, setSearchTerm] = React.useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 300);
const handleSearch = useCallback((text) => {
setSearchTerm(text);
}, []);
React.useEffect(() => {
if (debouncedSearchTerm) {
// Perform search
}
}, [debouncedSearchTerm]);
return (
<TextInput
value={searchTerm}
onChangeText={handleSearch}
placeholder="Search..."
/>
);
}
10. Advanced Topics
Server-Side Rendering (SSR)
Implement SSR for better SEO and initial load performance:
// server/ssr.js
const React = require('react');
const { renderToString } = require('react-dom/server');
const { AppRegistry } = require('react-native-web');
const express = require('express');
const App = require('../App').default;
// Register the app
AppRegistry.registerComponent('App', () => App);
const app = express();
app.get('*', (req, res) => {
const { element, getStyleElement } = AppRegistry.getApplication('App', {
initialProps: {},
rootTag: 'app-root'
});
// Render app to string
const appHtml = renderToString(element);
const appStyles = renderToString(getStyleElement());
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Native Web SSR</title>
${appStyles}
</head>
<body>
<div id="app-root">${appHtml}</div>
<script src="/bundle.js"></script>
</body>
</html>
`;
res.send(html);
});
app.listen(3000, () => {
console.log('SSR server running on port 3000');
});
Micro-Frontend Architecture
Scale large applications using micro-frontend patterns:
// Module federation for React Native Web
// webpack.config.js
const ModuleFederationPlugin = require('@module-federation/webpack');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
'user-app': 'userApp@http://localhost:3001/remoteEntry.js',
'product-app': 'productApp@http://localhost:3002/remoteEntry.js',
},
}),
],
};
// Lazy load micro-frontends
const UserApp = React.lazy(() => import('user-app/App'));
const ProductApp = React.lazy(() => import('product-app/App'));
function ShellApp() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="Users"
component={() => (
<Suspense fallback={<LoadingSpinner />}>
<UserApp />
</Suspense>
)}
/>
<Stack.Screen
name="Products"
component={() => (
<Suspense fallback={<LoadingSpinner />}>
<ProductApp />
</Suspense>
)}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Conclusion
React Native Web enables true universal app development, allowing you to share up to 95% of your code across mobile and web platforms. By following the best practices outlined in this guide, you can build performant, accessible, and maintainable applications that provide consistent user experiences across all platforms.
- ✅ Use React Native Web for maximum code sharing
- ✅ Optimize for web-specific performance considerations
- ✅ Implement responsive design patterns
- ✅ Handle platform-specific code gracefully
- ✅ Test across all target platforms
- ✅ Consider PWA features for enhanced web experience
As React Native Web continues to evolve, it's becoming the go-to solution for teams looking to build cross-platform applications efficiently. Start with a small project to get familiar with the ecosystem, then gradually adopt it for larger applications as your team gains experience.
🚀 Ready to catch UI issues before your users do?
Use Viewlytics to automatically capture and analyze your app's UI across real devices. Our AI-powered platform helps you identify visual bugs, layout issues, and inconsistencies before they impact user experience.
Start UI Testing with Viewlytics