Serving local HTML in WebView

SDK Version: latest stable
Platforms: Android

Hey, I am desparetly looking for a way to run a single page webapplication in WebView, with the corresponding files being stored locally.

I was able to achieve what I want while being in development mode and operating the app through Expo Go.

But as soon as I build the app into an APK and install it on an Android device, it stops working.

This is my code:

import React from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, SafeAreaView } from 'react-native';
import { WebView } from 'react-native-webview';

export default function App() {

  const webViewProps = {
    javaScriptEnabled: true,
    androidLayerType: 'hardware',
    originWhitelist: ['*'],
    allowFileAccess: true,
    domStorageEnabled: true,
    mixedContentMode: 'always',
    allowUniversalAccessFromFileURLs: true,
    scalesPageToFit: true,
  };

  const Header = () => (
    <View style={styles.header}>
    </View>
  );

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <StatusBar style="light" />
      <Header />
      <WebView
        style={styles.container}
        source={require('./assets/out/index.html')}
        {...webViewProps}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  header: {
    paddingTop: 15,
    paddingBottom: 10,
    backgroundColor: "#ec3c3c"
  },
  title: {
    color: "#fff",
    fontSize: 20,
    fontWeight: "bold",
    textAlign: "center"
  },
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
    paddingTop: 40,
  },
});

My best guess is, that the HTML files are somehow not included in the app bundle or are located somewhere, where WebView is unable to find them.

Do you guys have any idea on how I can solve this?
Do I need a local http-server? Do I need to serve the files in a different way?
I am open to all suggestions, except for hosting the HTML on a remote server, as I need the app to work offline.

Additionally: The solution has to work for Android and iOS.

Hi @changeowu

According to the WebView docs, you might indeed need to use a local HTTP server:

Have a look at the linked issues as well.

1 Like

Thanks for you reply!
I found that, too. But so far I have not been able to get this to run in my Expo project!
The team of “react-native-static-server” does not really support Expo.

If it works in a plain React Native app it should also work in Expo (but you might need to add a config plugin or something like that.)

It’s not clear to me what exactly you tried or what you wanted to do, but were unable to do in an Expo app.

EDIT:

About react-native-static-server: It should work out of the box, as long as you do not try to use it in the Expo Go app. You would need to create a development build and use that instead of Expo Go.

1 Like

My workflow was the following:
yarn expo prebuild
followed by yarn expo install @dr.pogodin/react-native-static-server

I then installed “cmake” in Android Studio.

Then I implemented this code:

import React, { useEffect, useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, SafeAreaView } from 'react-native';
import { WebView } from 'react-native-webview';
import StaticServer from "@dr.pogodin/react-native-static-server";

export default function App() {

  const server = useRef(new StaticServer(5555, "./assets/out", { localOnly: true, keepAlive: true })).current;
  const [uri] = useState(`http://localhost:${server.port}`);

  useEffect(() => {
    server.start().then((url) => {
      console.log("Serving at URL", url);
    });

    () => {
      // Stop the server
      server.stop();
    }
  }, []);

  const webViewProps = {
    javaScriptEnabled: true,
    androidLayerType: 'hardware',
    originWhitelist: ['*'],
    allowFileAccess: true,
    domStorageEnabled: true,
    mixedContentMode: 'always',
    allowUniversalAccessFromFileURLs: true,
    scalesPageToFit: true,
  };

  const Header = () => (
    <View style={styles.header}>
    </View>
  );

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <StatusBar style="light" />
      <Header />
      <WebView
        style={styles.container}
        source={{ uri }}
        {...webViewProps}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  header: {
    paddingTop: 15,
    paddingBottom: 10,
    backgroundColor: "#ec3c3c"
  },
  title: {
    color: "#fff",
    fontSize: 20,
    fontWeight: "bold",
    textAlign: "center"
  },
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
    paddingTop: 40,
  },
});

An then I did yarn expo run:android

The following errors showed up:

The Kotlin Gradle plugin was loaded multiple times in different subprojects, which is not supported and may break the build. 
This might happen in subprojects that apply the Kotlin plugins with the Gradle 'plugins { ... }' DSL if they specify explicit versions, even if the versions are equal.
Please add the Kotlin plugin to the common parent project or the root project, then remove the versions in the subprojects.
If the parent project does not need the plugin, add 'apply false' to the plugin line.
See: https://docs.gradle.org/current/userguide/plugins.html#sec:subprojects_plugins_dsl
The Kotlin plugin was loaded in the following projects: ':expo', ':expo-modules-core', ':react-native-webview'

> Task :dr.pogodin_react-native-static-server:buildCMakeDebug[arm64-v8a][lighttpd] FAILED
C/C++: ninja: Entering directory `/home/matthias/Arbeit/change/Wakaru/wakaru-expo/node_modules/@dr.pogodin/react-native-static-server/android/.cxx/Debug/3a3sm4k3/arm64-v8a'
C/C++: ld: error: undefined symbol: glob
C/C++: ld: error: undefined symbol: globfree
C/C++: clang: error: linker command failed with exit code 1 (use -v to see invocation)

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':dr.pogodin_react-native-static-server:buildCMakeDebug[arm64-v8a][lighttpd]'.
> com.android.ide.common.process.ProcessException: ninja: Entering directory `/home/matthias/Arbeit/change/Wakaru/wakaru-expo/node_modules/@dr.pogodin/react-native-static-server/android/.cxx/Debug/3a3sm4k3/arm64-v8a'
  [1/1] Linking C shared library ../../../../build/intermediates/cxx/Debug/3a3sm4k3/obj/arm64-v8a/liblighttpd.so
  FAILED: ../../../../build/intermediates/cxx/Debug/3a3sm4k3/obj/arm64-v8a/liblighttpd.so 

What version of the NDK do you have installed?

1 Like

I have three versions installed:

23.1.7779620
25.2.9519653
26.0.10792818

But I am not sure which one is used. :face_with_peeking_eye:

hmmm… I’m not sure either.

When I googled this part of the error:

C/C++: ld: error: undefined symbol: globfree

The most likely result was about old versions of the NDK. But I am pretty sure that all of the versions you listed should be new enough.

1 Like

That’s exactly what I got as well :neutral_face:

I guess I’m going to open an issue on github.

I found the solution.

minSdkVersion needs to be set to 28

In the file /android/build.gradle thefollowing needs to be changed:

buildscript {
    ext {
        minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '28')
    }
}

Great :slight_smile:

See also BuildProperties - Expo Documentation

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.