🚀 Advanced React Native WebView App - Complete Package

✨ Advanced Features Included: Pull-to-refresh, back button handling, offline detection, share functionality, deep linking, error handling, download support, file upload, progress bar, custom headers, cookie management, and much more!

📑 Table of Contents

📦 Step 1: Project Setup & Dependencies

# Create new React Native project
npx create-expo-app@latest

# Navigate to project
cd app-name

# Install all required dependencies
npm install react-native-webview
npm install @react-native-community/netinfo
npm install react-native-share
npm install react-native-fs
npm install react-native-splash-screen
npm install @react-navigation/native
npm install @react-navigation/native-stack
npm install react-native-screens
npm install react-native-safe-area-context
npm install react-native-permissions

# For iOS (Mac only)
cd ios && pod install && cd ..

📝 Step 2: Main App Component (App.js)

📄 App.js
import React, { useEffect, useState, useRef } from 'react';
import {
  SafeAreaView,
  StatusBar,
  StyleSheet,
  View,
  BackHandler,
  Alert,
  Platform,
  Linking,
  TouchableOpacity,
  Text,
} from 'react-native';
import { WebView } from 'react-native-webview';
import NetInfo from '@react-native-community/netinfo';
import Share from 'react-native-share';
import SplashScreen from './components/SplashScreen';
import OfflineNotice from './components/OfflineNotice';

const App = () => {
  // State management
  const [showSplash, setShowSplash] = useState(true);
  const [isLoading, setIsLoading] = useState(true);
  const [loadProgress, setLoadProgress] = useState(0);
  const [isConnected, setIsConnected] = useState(true);
  const [canGoBack, setCanGoBack] = useState(false);
  const [currentUrl, setCurrentUrl] = useState('');
  
  const webViewRef = useRef(null);

  // 🔴 CONFIGURE YOUR WEBSITE
  const WEBSITE_URL = 'https://your-website.com';
  const SPLASH_DURATION = 3000;

  // Network connectivity monitoring
  useEffect(() => {
    const unsubscribe = NetInfo.addEventListener(state => {
      setIsConnected(state.isConnected);
      if (!state.isConnected) {
        Alert.alert('No Internet', 'Please check your connection');
      }
    });

    return () => unsubscribe();
  }, []);

  // Splash screen timer
  useEffect(() => {
    const timer = setTimeout(() => {
      setShowSplash(false);
    }, SPLASH_DURATION);

    return () => clearTimeout(timer);
  }, []);

  // Android back button handler
  useEffect(() => {
    const backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      handleBackPress
    );

    return () => backHandler.remove();
  }, [canGoBack]);

  // Handle back button press
  const handleBackPress = () => {
    if (canGoBack && webViewRef.current) {
      webViewRef.current.goBack();
      return true;
    }
    
    Alert.alert(
      'Exit App',
      'Do you want to exit?',
      [
        { text: 'Cancel', style: 'cancel' },
        { text: 'Exit', onPress: () => BackHandler.exitApp() }
      ]
    );
    return true;
  };

  // Handle navigation state changes
  const handleNavigationStateChange = (navState) => {
    setCanGoBack(navState.canGoBack);
    setCurrentUrl(navState.url);
  };

  // Handle URL opening (external links)
  const handleShouldStartLoadWithRequest = (request) => {
    // Handle external links
    if (request.url.startsWith('http://') || request.url.startsWith('https://')) {
      // Check if it's your domain
      if (!request.url.includes(WEBSITE_URL.replace('https://', '').replace('http://', ''))) {
        Linking.openURL(request.url);
        return false;
      }
    }

    // Handle phone calls
    if (request.url.startsWith('tel:')) {
      Linking.openURL(request.url);
      return false;
    }

    // Handle emails
    if (request.url.startsWith('mailto:')) {
      Linking.openURL(request.url);
      return false;
    }

    return true;
  };

  // Share functionality
  const handleShare = async () => {
    try {
      await Share.open({
        title: 'Share this app',
        message: `Check out this amazing app: ${currentUrl}`,
        url: currentUrl,
      });
    } catch (error) {
      console.log('Error sharing:', error);
    }
  };

  // Refresh page
  const handleRefresh = () => {
    if (webViewRef.current) {
      webViewRef.current.reload();
    }
  };

  // JavaScript to inject into WebView
  const injectedJavaScript = `
    // Disable zoom
    const meta = document.createElement('meta');
    meta.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no');
    meta.setAttribute('name', 'viewport');
    document.getElementsByTagName('head')[0].appendChild(meta);

    // Send messages to React Native
    window.ReactNativeWebView.postMessage(JSON.stringify({
      type: 'PAGE_LOADED',
      url: window.location.href
    }));

    true;
  `;

  // Handle messages from WebView
  const handleMessage = (event) => {
    try {
      const data = JSON.parse(event.nativeEvent.data);
      console.log('Message from WebView:', data);
    } catch (error) {
      console.log('Error parsing message:', error);
    }
  };

  // Show splash screen
  if (showSplash) {
    return ;
  }

  return (
    
      
      
      {/* Offline Notice */}
      {!isConnected && }

      {/* Loading Progress Bar */}
      {isLoading && (
        
          
        
      )}

      {/* Action Buttons */}
      
        
          🔄
        
        
          📤
        
        {canGoBack && (
           webViewRef.current?.goBack()}>
            ⬅️
          
        )}
      

      {/* WebView */}
       {
          setIsLoading(true);
          setLoadProgress(0);
        }}
        onLoadProgress={({ nativeEvent }) => {
          setLoadProgress(nativeEvent.progress);
        }}
        onLoadEnd={() => {
          setIsLoading(false);
          setLoadProgress(1);
        }}
        
        // Error handling
        onError={(syntheticEvent) => {
          const { nativeEvent } = syntheticEvent;
          console.warn('WebView error:', nativeEvent);
          Alert.alert('Error', 'Failed to load page. Please try again.');
        }}
        
        // Navigation
        onNavigationStateChange={handleNavigationStateChange}
        onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest}
        
        // Message handling
        onMessage={handleMessage}
        injectedJavaScript={injectedJavaScript}
        
        // Settings
        javaScriptEnabled={true}
        domStorageEnabled={true}
        startInLoadingState={true}
        scalesPageToFit={true}
        allowsBackForwardNavigationGestures={true}
        allowsInlineMediaPlayback={true}
        mediaPlaybackRequiresUserAction={false}
        
        // Security
        mixedContentMode="compatibility"
        allowFileAccess={false}
        
        // Performance
        cacheEnabled={true}
        cacheMode="LOAD_DEFAULT"
        
        // Pull to refresh (iOS)
        bounces={true}
        pullToRefreshEnabled={true}
        
        // Custom headers
        // headers={{
        //   'Custom-Header': 'value'
        // }}
      />
    
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#ffffff',
  },
  webview: {
    flex: 1,
  },
  progressBar: {
    height: 3,
    width: '100%',
    backgroundColor: '#e0e0e0',
  },
  progress: {
    height: '100%',
    backgroundColor: '#667eea',
  },
  actionBar: {
    flexDirection: 'row',
    justifyContent: 'flex-end',
    padding: 10,
    backgroundColor: '#f7fafc',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  actionButton: {
    marginLeft: 15,
    padding: 8,
  },
  actionText: {
    fontSize: 20,
  },
});

export default App;

🎨 Step 3: Splash Screen Component

Create folder: components and create file SplashScreen.js

📄 components/SplashScreen.js
import React, { useEffect, useRef } from 'react';
import {
  View,
  Text,
  Image,
  StyleSheet,
  Animated,
  StatusBar,
  Dimensions,
} from 'react-native';

const { width, height } = Dimensions.get('window');

const SplashScreen = () => {
  const fadeAnim = useRef(new Animated.Value(0)).current;
  const scaleAnim = useRef(new Animated.Value(0.3)).current;

  useEffect(() => {
    Animated.parallel([
      Animated.timing(fadeAnim, {
        toValue: 1,
        duration: 1000,
        useNativeDriver: true,
      }),
      Animated.spring(scaleAnim, {
        toValue: 1,
        friction: 4,
        useNativeDriver: true,
      }),
    ]).start();
  }, []);

  return (
    
      
      
      {/* Animated Background */}
      
      
      
      {/* Logo */}
      
        
      

      {/* App Name */}
      
        My Awesome App
      

      {/* Tagline */}
      
        Your tagline here
      

      {/* Loading Indicator */}
      
        
        
        
      

      {/* Footer */}
      
        © 2024 Your Company
      
    
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#667eea',
    justifyContent: 'center',
    alignItems: 'center',
    position: 'relative',
  },
  backgroundCircle1: {
    position: 'absolute',
    width: width * 1.5,
    height: width * 1.5,
    borderRadius: width * 0.75,
    backgroundColor: 'rgba(118, 75, 162, 0.3)',
    top: -width * 0.5,
    right: -width * 0.3,
  },
  backgroundCircle2: {
    position: 'absolute',
    width: width * 1.2,
    height: width * 1.2,
    borderRadius: width * 0.6,
    backgroundColor: 'rgba(118, 75, 162, 0.2)',
    bottom: -width * 0.4,
    left: -width * 0.2,
  },
  logoContainer: {
    marginBottom: 30,
  },
  logo: {
    width: 150,
    height: 150,
  },
  appName: {
    fontSize: 32,
    fontWeight: 'bold',
    color: '#ffffff',
    marginBottom: 10,
    textAlign: 'center',
  },
  tagline: {
    fontSize: 16,
    color: 'rgba(255, 255, 255, 0.8)',
    marginBottom: 40,
    textAlign: 'center',
  },
  loadingContainer: {
    flexDirection: 'row',
    marginTop: 20,
  },
  loadingDot: {
    width: 10,
    height: 10,
    borderRadius: 5,
    backgroundColor: '#ffffff',
    marginHorizontal: 5,
    opacity: 0.7,
  },
  loadingDotDelay1: {
    opacity: 0.5,
  },
  loadingDotDelay2: {
    opacity: 0.3,
  },
  footer: {
    position: 'absolute',
    bottom: 30,
    fontSize: 12,
    color: 'rgba(255, 255, 255, 0.6)',
  },
});

export default SplashScreen;

📡 Step 4: Offline Notice Component

📄 components/OfflineNotice.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

const OfflineNotice = () => {
  return (
    
      ⚠️ No Internet Connection
    
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#ff6b6b',
    padding: 10,
    alignItems: 'center',
  },
  text: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '600',
  },
});

export default OfflineNotice;

🤖 Step 5: Android Configuration

5.1 AndroidManifest.xml

Edit android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Permissions -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />

    <application
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:allowBackup="false"
      android:theme="@style/AppTheme"
      android:usesCleartextTraffic="true"
      android:hardwareAccelerated="true">
      
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
        android:launchMode="singleTask"
        android:windowSoftInputMode="adjustResize"
        android:exported="true">
        
        <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <!-- Deep Linking -->
        <intent-filter android:autoVerify="true">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="https" android:host="your-website.com" />
        </intent-filter>
      </activity>

      <!-- File Provider for file sharing -->
      <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/file_paths" />
      </provider>
    </application>
</manifest>

5.2 build.gradle

Edit android/app/build.gradle - Add these to dependencies:

dependencies {
    implementation "com.facebook.react:react-native:+"
    
    // WebView
    implementation project(':react-native-webview')
    
    // Network Info
    implementation project(':react-native-community_netinfo')
}

🍎 Step 6: iOS Configuration

6.1 Info.plist

Edit ios/MyAdvancedWebApp/Info.plist

<dict>
    <!-- App Transport Security -->
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

    <!-- Camera Permission -->
    <key>NSCameraUsageDescription</key>
    <string>This app needs camera access to upload photos</string>

    <!-- Photo Library Permission -->
    <key>NSPhotoLibraryUsageDescription</key>
    <string>This app needs photo library access</string>

    <!-- Microphone Permission -->
    <key>NSMicrophoneUsageDescription</key>
    <string>This app needs microphone access</string>

    <!-- URL Schemes for Deep Linking -->
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>myapp</string>
            </array>
        </dict>
    </array>
</dict>

🔗 Step 7: Deep Linking (Optional)

📄 Add to App.js (after imports)
// Add this useEffect for deep linking
useEffect(() => {
  // Handle initial URL
  Linking.getInitialURL().then(url => {
    if (url && webViewRef.current) {
      webViewRef.current.injectJavaScript(
        `window.location.href = '${url}';`
      );
    }
  });

  // Handle URL updates
  const subscription = Linking.addEventListener('url', ({ url }) => {
    if (url && webViewRef.current) {
      webViewRef.current.injectJavaScript(
        `window.location.href = '${url}';`
      );
    }
  });

  return () => subscription.remove();
}, []);

🎨 Step 8: Assets & Icons

8.1 Create Assets Folder

# In project root
mkdir assets

# Add your logo
# Place logo.png (512x512 recommended) in assets folder

8.2 Android Icons

Replace icons in android/app/src/main/res/

8.3 iOS Icons

Use Xcode to add icons to ios/MyAdvancedWebApp/Images.xcassets/AppIcon.appiconset/

▶️ Step 9: Run the App

# Android
npx react-native run-android

# iOS
npx react-native run-ios

# Build Release APK (Android)
cd android
./gradlew assembleRelease
# APK location: android/app/build/outputs/apk/release/app-release.apk

✅ Pull to Refresh

Native pull-to-refresh gesture on iOS

⬅️ Back Button

Smart Android back button navigation

📡 Offline Mode

Real-time network status detection

📤 Share Feature

Native share dialog for content

📊 Progress Bar

Visual loading progress indicator

🔗 Deep Linking

Handle external URLs and app links

🎨 Animated Splash

Beautiful animated splash screen

⚡ Performance

Optimized caching and rendering

🔒 Security

Custom headers and cookie control

📱 Native Features

Camera, phone, email integration

🌐 Multi-Platform

Works seamlessly on iOS & Android

💬 JS Bridge

Two-way communication with website

⚙️ Customization Checklist

🔧 Advanced Customizations

Add File Download Support

// Add to WebView props
onFileDownload={({ nativeEvent }) => {
  const { downloadUrl } = nativeEvent;
  // Handle file download
  console.log('Download:', downloadUrl);
}}

Add Custom JavaScript Injection

// Modify existing injectedJavaScript
const injectedJavaScript = `
  // Hide specific elements
  const element = document.querySelector('.ads');
  if (element) element.style.display = 'none';
  
  // Add custom CSS
  const style = document.createElement('style');
  style.innerHTML = 'body { font-size: 16px !important; }';
  document.head.appendChild(style);
  
  // Track user interactions
  document.addEventListener('click', (e) => {
    window.ReactNativeWebView.postMessage(JSON.stringify({
      type: 'CLICK',
      target: e.target.tagName
    }));
  });
  
  true;
`;

Add Cookie Management

import CookieManager from '@react-native-cookies/cookies';

// Clear cookies
CookieManager.clearAll().then(() => {
  console.log('Cookies cleared');
});

// Get cookies
CookieManager.get('https://your-site.com').then((cookies) => {
  console.log('Cookies:', cookies);
});

🐛 Troubleshooting Guide

Common Issues & Solutions

Issue: White screen on Android
Solution: Check internet permission in AndroidManifest.xml and verify URL is correct
Issue: Logo not showing
Solution: Ensure logo.png exists in assets folder with correct path
Issue: Back button doesn't work
Solution: Make sure BackHandler is properly configured in useEffect
Issue: App crashes on iOS
Solution: Run cd ios && pod install and rebuild
Issue: Network request failed
Solution: Add NSAppTransportSecurity to Info.plist (iOS) or usesCleartextTraffic (Android)

📦 Additional Package Installation

# If you need more features, install these:

# Push Notifications
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging

# Analytics
npm install @react-native-firebase/analytics

# Geolocation
npm install @react-native-community/geolocation

# AsyncStorage (for local data)
npm install @react-native-async-storage/async-storage

# Biometric Authentication
npm install react-native-biometrics

# QR Code Scanner
npm install react-native-camera

# In-App Browser
npm install react-native-inappbrowser-reborn

🚀 Performance Optimization Tips

📱 Build for Production

Android Release Build

# Generate signing key
keytool -genkeypair -v -storetype PKCS12 -keystore my-app.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

# Edit android/gradle.properties
MYAPP_UPLOAD_STORE_FILE=my-app.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****

# Build
cd android
./gradlew bundleRelease

# Output: android/app/build/outputs/bundle/release/app-release.aab

iOS Release Build

# Open Xcode
open ios/MyAdvancedWebApp.xcworkspace

# In Xcode:
# 1. Select Product > Archive
# 2. Organizer will open
# 3. Select "Distribute App"
# 4. Follow App Store submission steps

🎉 You're All Set!

You now have a production-ready React Native app with all advanced features including:

📚 Next Steps

  1. Clone or create the project structure
  2. Install all dependencies
  3. Configure your website URL
  4. Add your branding (logo, colors, name)
  5. Test on both Android and iOS
  6. Configure deep linking for your domain
  7. Set up analytics and crash reporting
  8. Create release builds
  9. Submit to Google Play Store and Apple App Store

🎯 Ready to Launch!

React Native WebView app is ready for production

zenbyt web to app conversion documentation