'react-native webview loading indicator

I am trying to show loading indicator in webweb as follow. Loading indicator is showing but there is white background show after page is loaded. If I change to startInLoadingState to false, web content is showing but loading indicator does not show. It is happing in "react-native": "0.46.3" on ios

renderLoadingView() {
      return (
          <ActivityIndicator
             animating = {this.state.visible}
             color = '#bc2b78'
             size = "large"
             style = {styles.activityIndicator}
             hidesWhenStopped={true} 
          />
      );
}
<WebView
    source={source} 
    renderLoading={this.renderLoadingView} startInLoadingState={true} />


Solution 1:[1]

I like this approach which shows the activity indicator overlayed on the loading Webview so you don't have to wait until the entire page is loaded to start seeing content.

constructor(props) {
  super(props);
  this.state = { visible: true };
}

hideSpinner() {
  this.setState({ visible: false });
}

render() {
  return (
    <View style={{ flex: 1 }}>
      <WebView
        onLoad={() => this.hideSpinner()}
        style={{ flex: 1 }}
        source={{ uri: this.props.navigation.state.params.url }}
      />
      {this.state.visible && (
        <ActivityIndicator
          style={{ position: "absolute", top: height / 2, left: width / 2 }}
          size="large"
        />
      )}
    </View>
  );
}

Solution 2:[2]

A nice approach is setting the property startInLoadingState to true and set the renderLoading to return the desired View. See the example below.

displaySpinner() {
  return (
    <View>
      {/* Your spinner code goes here. 
        This one commes from react-native-material-kit library */}
      <SingleColorSpinner />
    </View>
  );
}

render() {
  return (
    <WebView
      startInLoadingState={true}
      source={{ uri: this.state.myUri }}
      renderLoading={() => {
        return this.displaySpinner();
      }}
    />
  );
}

Solution 3:[3]

I have steped on that problem and after some research i found a pretty good solution.

It requires the "react-native-loading-spinner-overlay"

npm install --save react-native-loading-spinner-overlay

index.android.js

import Spinner from 'react-native-loading-spinner-overlay';

const main = 'http://www.myURI.pt';

class MyApp extends Component {
    constructor(props) {
        super(props);
        this.state = { uri: main, visible: true };
    }

    showSpinner() {
        console.log('Show Spinner');
        this.setState({ visible: true });
    }

    hideSpinner() {
        console.log('Hide Spinner');
        this.setState({ visible: false });
    }

    render() {
        return (
            <View>
                <Spinner
                    visible={this.state.visible}
                    textContent={'Loading...'}
                    textStyle={{ color: '#FFF' }}
                />
                <WebView
                    scalesPageToFit
                    source={{ uri: this.state.uri }}
                    onLoadStart={() => (this.showSpinner())}
                    onLoad={() => (this.hideSpinner())}
                />
            </View>
        );
    }
}

I think i didn't miss any line.

Solution 4:[4]

Alter your renderLoadingView function to the following, and the loading indicator should work as desired:

renderLoadingView() {
  return (
    <ActivityIndicator
      color='#bc2b78'
      size='large'
      styles={styles.activityIndicator}
    />
  );
}

So essentially, just remove the animating (as it is not required for the given usage) and hidesWhenStopped props from your ActivityIndicator. Hope this helps.

Solution 5:[5]

Copy & Pasteable: Minimal Webview Component with Loading Indicator

import React, { Component } from "react";
import { ActivityIndicator} from "react-native";
import { WebView } from "react-native-webview";


// Pass a "uri" prop as the webpage to be rendered
class WebViewScreen extends Component {
  constructor(props) {
    super(props);
    this.state = { visible: true };
  }
  hideSpinner() {
    this.setState({ visible: false });
  }
  render() {
    return (
      <React.Fragment>
        <WebView
          onLoadStart={() => this.setState({ visible: true })}
          onLoadEnd={() => this.setState({ visible: false })}

          // Pass uri in while navigating with react-navigation. To reach this screen use:
          // this.props.navigation.navigate("WebViewScreen", {uri: "google.ca"});
          source={{ uri: this.props.navigation.state.params.uri }} 
        />
        {this.state.visible ? (
          <ActivityIndicator
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              right: 0,
              bottom: 0,
              jusityContent: "space-around",
              flexWrap: "wrap",
              alignContent: "center",
            }}
            size="large"
          />
        ) : null}
      </React.Fragment>
    );
  }
}
export default WebViewScreen;

Solution 6:[6]

I've used @AdamG's solution but there have a problem with absolute path. The below solution is set ActivityIndicator to the center but with a different way.

<View style={{ flex: 1 }}>
            <WebView
                onLoad={() => this.hideSpinner()}
                style={{ flex: 1 }}
                source={{ uri: 'yourhtml.html' }}
            />
            <View style={{backgroundColor:'white', height:1}}></View>
            {this.state.visible && (
                <View style={{flex:1, alignItems:'center'}}>
                    <ActivityIndicator
                        size="large"
                    />
                </View>
            )}
          </View>

There is 2 another {flex:1} View and ActivityIndicator is in top of the bottom View. I've centered that.

  <View style={{backgroundColor:'white', height:1}}></View>

And this line is set the opacity when you have loading state, there have two different View. In top view there is WebView and there is a black bottom border View belong to the WebView.For closing I've patched it with a white helper view.

Solution 7:[7]

Hey bro this is my solution, you have to use the event onLoadEnd instead onLoad, the event onLoad is not working for me.

import React, { Component } from 'react';
import { StyleSheet, ActivityIndicator, View } from 'react-native';
import { WebView } from "react-native-webview";

export default class MainActivity extends Component {
  constructor(props) {
    super(props);
    this.state = { visible: true };
  }

  showSpinner() {
    console.log('Show Spinner');
    this.setState({ visible: true });
  }

  hideSpinner() {
    console.log('Hide Spinner');
    this.setState({ visible: false });
  }

  render() {
    return (
      <View
        style={this.state.visible === true ? styles.stylOld : styles.styleNew}>
        {this.state.visible ? (
          <ActivityIndicator
            color="#009688"
            size="large"
            style={styles.ActivityIndicatorStyle}
          />
        ) : null}

        <WebView
          style={styles.WebViewStyle}
          //Loading URL
          source={{ uri: 'https://aboutreact.com' }}
          //Enable Javascript support
          javaScriptEnabled={true}
          //For the Cache
          domStorageEnabled={true}
          //View to show while loading the webpage
          //Want to show the view or not
          //startInLoadingState={true}
          onLoadStart={() => this.showSpinner()}
          onLoad={() => this.hideSpinner()}
        />
      </View>
    );
  }
}
const styles = StyleSheet.create({
  stylOld: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  styleNew: {
    flex: 1,
  },
  WebViewStyle: {
    justifyContent: 'center',
    alignItems: 'center',
    flex: 1,
    marginTop: 40,
  },
  ActivityIndicatorStyle: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    position: 'absolute',
  },
});

Solution 8:[8]

react-native webview is now deprecated.
You can import react-native-webview and do the following:

    <WebView
    source={{ uri: 'https://reactnative.dev' }}
    startInLoadingState={true}
    renderLoading={() => <Loading />}
    />

Solution 9:[9]

If you want to show a Spinner and then replace that spinner with the WebView already loaded, this is your answer:


    import React from 'react';
    import { StyleSheet, ActivityIndicator, View } from 'react-native';
    import { WebView } from "react-native-webview";
    
    function MyApp() {
    const Spinner = () => (
        <View style={styles.activityContainer}>
          <ActivityIndicator size="large" color={white} />
        </View>
    );
    
    return (
    <WebView
            bounces={false}
            startInLoadingState={true}
            renderLoading={Spinner}
            style={styles.container}
            source={{ uri: yourURL }}
            showsHorizontalScrollIndicator={false}
            scalesPageToFit
          />
    )
    }
    
    
    export default StyleSheet.create({
      container: {
        flex: 1
      },
      activityContainer: {
        alignItems: 'center',
        justifyContent: 'center',
        position: 'absolute',
        top: 0,
        left: 0,
        backgroundColor: black,
        height: '100%',
        width: '100%'
      }
    });

Solution 10:[10]

<WebView style={{ flex: 1 }} startInLoadingState={true} source={{ uri: "https://google.com" }} renderLoading={() => ( )} />

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 GollyJer
Solution 2 Mosh Feu
Solution 3
Solution 4
Solution 5
Solution 6 eemrah
Solution 7
Solution 8 Mark Reid
Solution 9
Solution 10 Shohel Ahamad