Open In App

Create Web Browser using React-Native

Last Updated : 08 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we will build the Web Browser using the React Native language. In this application, there will be as search bar with the button. The uer can enter the search text or the URL and click on the button, the browser will take to the desired result. Also there are morre naivtional buttons like Back, Forward, History, Clear History, Refresh, Stop. All the navigations make the user interface and functionality of using Browser more impactive.

Preview of final output: Let us have a look at how the final output will look like.

IMG_4461-(2)

Prerequisites

Approach to create Web Browser

The below code snippet is built in React Native, which is a basic and simple web browser. Here we have used the useState to manage the state of the application and also the useRef to update the DOM. Here, the browser has the search box and the button as the core components. The user can enter the search query and get the results of the searched text. There are also other navigations like Back, Forward, History, Clear History, Reload, Stop, Along with this, the user can zoom in and zoom out on the page using finger gestures.

Steps to install & configure React Native:

Step 1: Create a react native application by using this command:

npx create-expo-app web-browser

Step 2: After creating your project folder, i.e. web-browser, use the following command to navigate to it:

cd web-browser

Step 3: Install required modules via following command in Terminal.

npm i react-native-vector-icons react-native-web react-native-webview

The updated dependencies in package.json file will look like:

  "dependencies": {
"@expo/webpack-config": "^19.0.0",
"expo": "~49.0.13",
"expo-status-bar": "~1.6.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.5",
"react-native-vector-icons": "^10.0.0",
"react-native-web": "~0.19.6",
"react-native-webview": "^13.6.2"
},


Project Structure

PS

Example: Implementation of above approach using React JS. Functionalities contains by files as follows:

  • App.js : It is a parent component which calls other components in it.
  • WebViewComponent.js : contains the functionality to display webpage.
  • HistoryModal.js: Contains history functionality of whole application like clear history or display history.
  • Styles.css: Contains styling of whole application.

Javascript




// App.js
import React, { useState, useRef } from 'react';
import
    { View, Text, TextInput, TouchableOpacity, ActivityIndicator, Modal, Alert }
        from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import { PanResponder } from 'react-native';
import WebViewComponent from './WebViewComponent';
import HistoryModal from './HistoryModal';
import styles from './styles';
const App = () => {
    const [url, setUrl] = useState(
    const [prev, setPrev] = useState(false);
    const [next, setNext] = useState(false);
    const [input, setInput] = useState('');
    const webviewRef = useRef(null);
    const [loading, setLoading] = useState(true);
    const [history, setHistory] = useState([]);
    const [histShow, setHistShow] = useState(false);
    const [currscale, setCurrScale] = useState(1);
    const [zoom, setZoom] = useState(false);
    const panResponder = PanResponder.create({
        onMoveShouldSetPanResponder: (evt, gestureState) => {
            return gestureState.numberActiveTouches === 2;
        },
        onPanResponderGrant: () => {
            setZoom(true);
        },
        onPanResponderMove: (evt, gestureState) => {
            if (gestureState.numberActiveTouches === 2) {
                const distance = Math.sqrt(
                    Math.pow(gestureState.dx, 2) + Math.pow(gestureState.dy, 2)
                );
                const newScale = (currscale + (distance - 20) / 1000).toFixed(2);
                if (newScale >= 1) {
                    setCurrScale(newScale);
                }
            }
        },
        zoomFunction: () => {
            setZoom(false);
        },
    });
    const navStateFunction = (navState) => {
        setPrev(navState.canGoBack);
        setNext(navState.canGoForward);
        setHistory((prevHistory) => [navState.url, ...prevHistory]);
    };
    const prevFunction = () => {
        if (prev) {
            webviewRef.current.goBack();
        }
    };
 
    const nextFunction = () => {
        if (next) {
            webviewRef.current.goForward();
        }
    };
 
    const reloadFunction = () => {
        webviewRef.current.reload();
    };
 
    const stopFunction = () => {
        webviewRef.current.stopLoading();
    };
 
    const increaseFontSize = () => {
        webviewRef.current.injectJavaScript(`
      var style = document.body.style;
      style.fontSize = (parseFloat(style.fontSize) || 16) + 2 + 'px';
    `);
    };
    const decreaseFontSize = () => {
        webviewRef.current.injectJavaScript(`
      var style = document.body.style;
      style.fontSize = (parseFloat(style.fontSize) || 16) - 2 + 'px';
    `);
    };
    const urlVisitFunction = () => {
        const inputTrimmed = input.trim();
        if (inputTrimmed === '') {
            return;
        }
        if (/^(https?|ftp):\/\//i.test(inputTrimmed)) {
            setUrl(inputTrimmed);
        } else {
            if (inputTrimmed.match(/^(www\.)?[a-z0-9-]+(\.[a-z]{2,})+/)) {
                setUrl(`https://${inputTrimmed}`);
            } else {
                const searchQuery =
`https://www.google.com/search?q=${encodeURIComponent(inputTrimmed)}`;
                setUrl(searchQuery);
            }
        }
    };
    const histCleatFunction = () => {
        setHistory([]);
        Alert.alert('History Cleared', 'Your browsing history has been cleared.');
    };
    const loadHistFunction = () => {
        setHistShow(true);
    };
    return (
        <View style={styles.container}>
            <View style={styles.header}>
                <Text style={styles.headerText}>GeeksforGeeks</Text>
            </View>
            <Text style={styles.subHeaderText}>Web Browser in React Native</Text>
            <View style={styles.searchContainer}>
                <TextInput style={styles.textInput}
                           placeholder="Enter a URL or search query"
                           onChangeText={(text) => setInput(text)}/>
                <TouchableOpacity onPress={urlVisitFunction}
                                  style={styles.goButton}>
                    <Text style={styles.goButtonText}>Go</Text>
                </TouchableOpacity>
            </View>
            <View style={styles.toolbar}>
                <TouchableOpacity onPress={prevFunction}
                                  disabled={!prev}
                                  style={styles.navigationButton}>
                    <Icon name="arrow-left" size={18} color="black" />
                    <Text style={styles.iconText}>Back</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={nextFunction}
                                  disabled={!next}
                                  style={styles.navigationButton}>
                    <Icon name="arrow-right" size={18} color="black" />
                    <Text style={styles.iconText}>Forward</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={histCleatFunction}
                                  style={styles.clearButton}>
                    <Icon name="trash" size={18} color="black" />
                    <Text style={styles.iconText}>Clear</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={loadHistFunction}
                                  style={styles.historyButton}>
                    <Icon name="history" size={18} color="black" />
                    <Text style={styles.iconText}>History</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={reloadFunction}
                                  style={styles.reloadButton}>
                    <Icon name="refresh" size={18} color="black" />
                    <Text style={styles.iconText}>Reload</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={stopFunction}
                                  style={styles.stopButton}>
                    <Icon name="stop" size={18} color="black" />
                    <Text style={styles.iconText}>Stop</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={increaseFontSize}
                                  style={styles.fontButton}>
                    <Icon name="font" size={18} color="black" />
                    <Text style={styles.iconText}>+ Font</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={decreaseFontSize}
                                  style={styles.fontButton}>
                    <Icon name="font" size={18} color="black" />
                    <Text style={styles.iconText}>- Font</Text>
                </TouchableOpacity>
            </View>
            <WebViewComponent url={url}
                              prev={prev}
                              next={next}
                              loading={loading}
                              setLoading={setLoading}
                              webviewRef={webviewRef}
                              navStateFunction={navStateFunction}
                              reloadFunction={reloadFunction}
                              stopFunction={stopFunction}
                              increaseFontSize={increaseFontSize}
                              decreaseFontSize={decreaseFontSize}
                              zoom={zoom}
                              panResponder={panResponder}
                              currscale={currscale}/>
            <HistoryModal history={history}
                          histShow={histShow}
                          setHistShow={setHistShow}
                          setUrl={setUrl}/>
        </View>
    );
};
export default App;


Javascript




// WebViewComponent.js
import React from 'react';
import { View, ActivityIndicator } from 'react-native';
import { WebView } from 'react-native-webview';
import styles from './styles';
const WebViewComponent =
    ({ url,
       setLoading,
       loading,
       webviewRef,
       navStateFunction,
       zoom,
       panResponder,
       currscale }) => {
  return (
    <View style={styles.webviewContainer}>
      <WebView  ref={webviewRef}
                onNavigationStateChange={navStateFunction}
                source={{ uri: url }}
                onLoad={() => setLoading(false)}
                scalesPageToFit={false}
                javaScriptEnabled={true}
                bounces={false}
                startInLoadingState={true}
                originWhitelist={['*']}
                style={{transform: [{ scale: currscale }]}}
        {...(zoom ? panResponder.panHandlers : {})}/>
      {loading && (
        <View style={styles.loadingOverlay}>
          <ActivityIndicator size="large" color="blue" />
        </View>
      )}
    </View>
  );
};
export default WebViewComponent;


Javascript




// HistoryModal.js
import React from 'react';
import { View, Text, TouchableOpacity, FlatList, Modal }   
    from 'react-native';
import styles from './styles';
const HistoryModal = ({ history, histShow, setHistShow, setUrl }) => {
    return (
        <Modal animationType="slide"
               transparent={false}
               visible={histShow}>
            <View style={styles.modalContainer}>
                {history.length === 0 ? (
                    <Text style={styles.noHistoryText}>No History Present</Text>
                ) : (
                    <FlatList data={history}
                              keyExtractor={(i, index) => index.toString()}
                              renderItem={({ item }) => (
                        <TouchableOpacity onPress={() =>
                                setUrl(item)} style={styles.historyItem}>
                                <Text>{item}</Text>
                            </TouchableOpacity>)}/>)}
                <TouchableOpacity onPress={() => setHistShow(false)} style={styles.closeModalButton}>
                    <Text style={styles.closeModalButtonText}>Close</Text>
                </TouchableOpacity>
            </View>
        </Modal>
    );
};
export default HistoryModal;


Javascript




// Styles.js
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'white',
        padding: 10,
    },
    header: {
        marginTop: 20,
        alignItems: 'center',
    },
    headerText: {
        fontSize: 28,
        color: 'green',
    },
    subHeaderText: {
        fontSize: 16,
        color: 'black',
        textAlign: 'center',
        marginBottom: 10,
    },
    searchContainer: {
        flexDirection: 'row',
        alignItems: 'center',
        marginBottom: 10,
    },
    textInput: {
        flex: 1,
        height: 40,
        borderColor: 'gray',
        borderWidth: 1,
        marginRight: 10,
        paddingLeft: 10,
    },
    goButton: {
        backgroundColor: 'blue',
        padding: 10,
        borderRadius: 5,
        alignItems: 'center',
        width: 60,
    },
    goButtonText: {
        color: 'white',
    },
    toolbar: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        backgroundColor: 'lightgray',
        padding: 1,
    },
    navigationButton: {
        backgroundColor: 'transparent',
        padding: 10,
        borderRadius: 5,
    },
    clearButton: {
        backgroundColor: 'transparent',
        padding: 10,
        borderRadius: 5,
    },
    historyButton: {
        backgroundColor: 'transparent',
        padding: 10,
        borderRadius: 5,
    },
    webviewContainer: {
        flex: 1,
        position: 'relative',
    },
    loadingOverlay: {
        ...StyleSheet.absoluteFill,
        backgroundColor: 'rgba(255, 255, 255, 0.7)',
        justifyContent: 'center',
        alignItems: 'center',
    },
    modalContainer: {
        flex: 1,
        justifyContent: 'center',
        padding: 20,
    },
    historyItem: {
        padding: 10,
        borderBottomWidth: 1,
        borderBottomColor: 'lightgray',
    },
    closeModalButton: {
        backgroundColor: 'red',
        padding: 10,
        borderRadius: 5,
        alignItems: 'center',
        marginTop: 10,
    },
    closeModalButtonText: {
        color: 'white',
    },
    iconText: {
        color: 'black',
        textAlign: 'center',
        fontSize: 14,
    },
    noHistoryText: {
        fontSize: 18,
        color: 'black',
        textAlign: 'center',
        marginVertical: 20,
    },
    reloadButton: {
        backgroundColor: 'transparent',
        padding: 10,
        borderRadius: 5,
    },
    stopButton: {
        backgroundColor: 'transparent',
        padding: 10,
        borderRadius: 5,
    },
    fontButton: {
        backgroundColor: 'transparent',
        padding: 10,
        borderRadius: 5,
    },
});
export default styles;


Steps to run application:

Step 1: Run App via following command.

npx expo start

Step 2: Run command according to your Operating System.

  • Android
npx react-native run-android

  • IOS
npx react-native run-ios

Output:

Media_231013_121411



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads