Embedding a local website on your Expo/React Native project

Filipe Degrazia
3 min readAug 22, 2018

--

Photo by Blake Cheek on Unsplash

I spent the last few months working on a big project. This was a headache comparable to hitting my own head with a hammer.

Let’s say you have a website you don’t want to port to React Native. Or you want to use some library that isn’t available for React Native (Such as a rich text editor). However, most of the documentation for WebView supposes you are accessing remote websites. When you try to open a local .html file, you start running into problems.

Problem 1: How to format the html?

You might quickly realize that even if your website outside of React Native can require files as it wishes, for your “parasite” of a website, it doesn’t quite work that way. When it asks for its css and javascript, the app probably has already thrown it away, and if not, it will be very complex to actually find it. The easiest solution is to inline the css and minified js. The best solution is to configure webpack to do so. I actually just wrote a python script. If you don’t plan on changing it, you can copy it by hand too, substituting <script source=… tags for <script>/* your code */</script>, and doing the same for links to stylesheets.

Problem 2: How do you actually access it?

In the simulator, you can access any file in the project. Thus, source={require(./pathToFile.html)} works. However, when you build a standalone, it doesn’t work quite in the same way. I mean, at least for android it doesn’t. The android webView doesn’t recognise asset:/// uris for some reason. You have to get the file:/// path. Thankfully, that is very easy. The assets are bundled inside file:///android_asset (Careful, don’t write assets), and Expo.Asset.fromModule(require(‘./pathToFile.html')).localUri returns asset:///nameOfFile.html. But that’s not all. For the first few times, this uri will be correct. However, after a while, it changes into another file scheme, and can’t be accessed in the same way. Instead, you’ll have to access the localUri directly. Thus, the complete solution is:

/* Outside of return */const { localUri } = Expo.Asset.fromModule(require('./pathToFile.html'));/* On the webView */
source={
Platform.OS === ‘android’
? {
uri: localUri.includes('ExponentAsset')
? localUri
: ‘file:///android_asset/’ + localUri.substr(9),
}
: require(‘./pathToFile.html’)
}

(A constant part of the uri is ExponentAsset, that’s why I chose to check if that was part of it)

Problem 3: How do I make sure it is actually being bundled?

Even if we have everything configured correctly, we still need to bundle the assets with our app, if we want it to work offline on the first launch.
In app.json, we’re going to add the following atributes:

{
“expo”: {
// rest of your app.json
“assetBundlePatterns”: [
“assets/**”,
],
“packagerOpts”: {
“assetExts”: [“html”]
},
}
}

(Make sure to delete my comment, or the json will explode)

What this code is doing is bundling everything inside assets (including any directories inside it) with your app when you build it, and making sure that when it finds a .html in the assets folder, it treats it as an actual asset, and not something else.

And there we go. Hopefully, your app and your website should be up and running. If you have any suggestions or corrections, please leave a comment. I’d love to hear your thoughts!

--

--