Xaree Lee
Xaree Lee

Reputation: 3387

(React Native) Load local HTML file into WebView

I try to load the local .html file into WebView in React Native:

// load local .html file
const PolicyHTML = require('./Policy.html');

// PolicyHTML is just a number of `1`
console.log('PolicyHTML:', PolicyHTML);

// will cause an error: JSON value '1' of type NSNumber cannot be converted to NSString
<WebView html={PolicyHTML} />

The .html file should be read as a string, not as a resource representative.

How can I load the .html file into WebView in React Native?


By the way, what is the type of those resource representatives from require()? Is it number?

Upvotes: 50

Views: 88343

Answers (7)

Mihkuno
Mihkuno

Reputation: 129

If you're working with assets, project directories is different on the device's directory once the project is build and you can't simply reference them via string url.

Expo

If using expo, you have to require every asset then use useAssets on the require to cache them to the local storage of the device.

useAssets will return an object that contains a localUri (this is the uri of the image that has been cached)

you can then use the localUri and put it as the src of the image

    import { useAssets } from 'expo-asset';
    /* . . . */
    const IndexHTML = require('./assets/index.html');
    const myImage = require('./assets/splash.png');

    // url link after image is cached to the device
    const [imgSrc, setImgSrc] = useState('');

    const [image, imerr] = useAssets(myImage);
    const [html, error] = useAssets(IndexHTML);

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

        onLoad: () => {
            console.log(image[0].localUri);
            setImgSrc(image[0].localUri);
        },
        source: {
            html: '<img src="' + imgSrc + '"/>',
        },
    };

    return <WebView {...webViewProps} />
    const webViewProps = {
        ...
        source: IndexHTML,
    };

Note: for the expo apporach, files referenced in IndexHTML will not be found

  1. The trick is to turn your html into a string literal to utilize template strings.

  2. Then you have to manually require each of those assets to concatenate localUrl

  3. require() has limited types supported and you need to add a metro.config.js in your root folder.

  4. it will give errors if you require() a .js file since it reads it as a module rather, the workaround approach would be to bundle your assets

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname);

config.resolver.assetExts.push(
    // Adds support for asset file types
    'css', 'ppt', 'obj'
);

module.exports = config;
  • Moreover, expo can hot reload changes done with the cached assets.

React Native

If you have the android folder in your directory, navigate to

android > app > build.gradle

then add

android {
    /* link assets to the local storage of device */
    sourceSets { 
        main { assets.srcDirs = ['src/main/assets', '../../source/assets/'] } 
        //                      [do not touch,    'relative to your asset'] }
    }
    . . .

finally, the relative folder you linked in gradle can be accessed through file:///android_asset/

for ex. file:///android_asset/index.html -> /asset/index.html

return <WebView source={{uri: `file:///android_asset/index.html`}} />
  • For IOS, here's how

  • On the other hand, you have to rebuild vanilla react to see the changes in the assets.. which takes about 20 minutes or so

Alternative

A quick solution would be to integrate a static server, but this is a recent fork of the react-native-static-server that only works in vanilla react native.

Upvotes: 4

Alexander
Alexander

Reputation: 138

With Expo tools and generally using Expo:

import { WebView } from "react-native-webview";
import { readAsStringAsync } from "expo-file-system";
import { useAssets } from "expo-asset";

export const MusicSheet = () => {
  const [index, indexLoadingError] = useAssets(
    require("../assets/musicsheetview/index.html")
  );

  const [html, setHtml] = useState("");

  if (index) {
    readAsStringAsync(index[0].localUri).then((data) => {
        setHtml(data);
    });
  }

  return (
    <View style={styles.container}>
      <WebView
        onLoad={() => {}}
        source={{ html }}
        onMessage={(event) => {}}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    height: 100,
    display: "flex",
  },
});

Upvotes: 4

Daniel Garmoshka
Daniel Garmoshka

Reputation: 6352

If you need to serve local assets as well, then:

  1. put all assets together with index.html into android/app/src/main/assets/www (You can copy them there with gradle task)

  2. Then:

  var uri = Platform.OS == "android" ?
    "file:///android_asset/www/index.html" :
    "./web/www/index.html"
  return <WebView source={{ uri }} />

** For iOS didn't tested, please add instruction, how assets should be stored

Upvotes: 2

Luca Davanzo
Luca Davanzo

Reputation: 21520

I come across this post searching for loading static html.
If your html code is retrieved using, for example, an API, you can render WebView in this way:

<WebView
    originWhitelist={['*']}
    source={{ html: html, baseUrl: '' }}
/>

Notice that originWhitelistis required as explained in the documentation:

Note that static html will require setting of originWhitelist for example to ["*"].

Upvotes: 17

stevemu
stevemu

Reputation: 101

<View style={{flex: 1}}>
    <WebView
      style={{flex: 1}}
      source={require("./resources/index.html")}
    />
</View>

To make WebView, the parent has to has a dimension or flex:1. We could set the WebView to flex: 1 too so that it fills up the parent.

Upvotes: 8

Pankaj Sharma
Pankaj Sharma

Reputation: 27

Try this :

  • Add your .html file in your project.
  • Write such lines of code in the file where you want to use WebView Component

    const OurStoryHTML = require ('./OurStory.html')

  • <WebView source={OurStoryHTML} style={{flex: 1, marginTop : 44}} />

It may help you.

Upvotes: 0

Fmussap
Fmussap

Reputation: 532

try it:

const PolicyHTML = require('./Policy.html');

<WebView
  source={PolicyHTML}
  style={{flex: 1}}
 />

Upvotes: 50

Related Questions