React Native: Offline Content Display with Local HTML Files in WebView
In today’s digital age, access to online content is often taken for granted. However, there are situations where internet connectivity may be limited or unavailable, leaving users unable to access vital information. In such cases, the ability to display offline content becomes crucial. React Native offers a solution to this challenge. By leveraging the WebView component, developers can seamlessly import and display local HTML files within their React Native applications. In this article, we’ll explore how to achieve offline content display using local HTML files in React Native WebView, empowering developers to create versatile and accessible mobile applications.
Isn’t React Native just a mobile webview based on React?
React Native is often misunderstood as being solely a mobile webview based on React, but this oversimplification doesn’t fully capture its capabilities. While React Native does leverage JavaScript, like web-based applications, it’s fundamentally different from a traditional webview-based approach. React Native utilizes a JavaScript runtime environment, similar to web technologies, but it also incorporates native components to render UI elements directly on the device. This hybrid approach enables React Native to achieve near-native performance and access to device-specific features, unlike traditional webviews. So, while React Native shares some similarities with web development, it goes beyond simply rendering web content in a mobile app, offering a powerful framework for building truly native cross-platform applications.
Regarding the display of HTML files, due to React Native’s implementation details, you can’t just write an HTML file and expect it to be displayed on a mobile phone. If you want to use a simple HTML file, you have to use it in combination with a WebView component. The WebView component acts as a container for displaying web content within the app, enabling you to load and render HTML files, as well as interact with them using JavaScript. This approach provides flexibility for incorporating web-based content into your React Native app while leveraging the power of native components for building versatile user interfaces.
How is it done?
To display HTML files in a React Native app using a WebView component, you first need to import the WebView from the ‘react-native-webview’ library. Then, you can use the WebView component in your JSX/TSX code and specify the source of the HTML file using the ‘source’ prop. This can be a local file path or a URL. In our case, we will use a local HTML file that displays a graph contained in a box. This the example my robot friend, ChatGPT(mostly :D) managed to write for me
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Line Graph</title>
<style>
body{
background: ghostwhite;
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* Style for the graph container */
#graph-container {
box-shadow: 0 2px 10px rgba(6, 120, 201, 0.25);
margin: 100px auto;
width: 300px;
height: 300px;
border: 1px solid rgba(6, 120, 201, 0.5);
}
</style>
</head>
<body>
<!-- Graph container -->
<div id="graph-container"></div>
<h3>Above is a graph</h3>
<script>
// Sample data for the graph
const data = [
{ x: 0, y: -100 },
{ x: 1, y: -90 },
{ x: 2, y: 0 },
{ x: 3, y: -10 },
{ x: 4, y: 60 },
{ x: 9, y: 80 }
];
// Function to create the line graph
function createGraph() {
// Get the graph container element
const container = document.getElementById('graph-container');
// Create an SVG element
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
// Create a polyline element for the line graph
const polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
polyline.setAttribute('fill', 'none');
polyline.setAttribute('stroke', 'green');
polyline.setAttribute('stroke-width', '2');
// Construct the points string for the polyline
const points = data.map(point => `${point.x * 50},${100 - point.y * 1.5}`).join(' ');
// Set the points attribute for the polyline
polyline.setAttribute('points', points);
// Append the polyline to the SVG element
svg.appendChild(polyline);
// Append the SVG element to the graph container
container.appendChild(svg);
}
// Call the createGraph function to generate the graph
createGraph();
</script>
</body>
</html>
This code works just fine on a browser:
Now let’s see how we can render this on a mobile phone using React Native!
I will skip the chit-chat and jump right into the code. First, we will need a .tsx/.jsx file in which we will use the React Native components. In my case, I decided to write this using Typescript(hence the .tsx extension) and with the help of the Compound Pattern:
import { createContext } from "react";
import { SafeAreaView } from "react-native";
import WebView from "react-native-webview";
interface ChartModel {}
const ChartContext = createContext<ChartModel>({});
interface ChartProviderProps {
children: React.ReactNode;
}
const ChartProvider: React.FC<ChartProviderProps> = (props) => {
return <ChartContext.Provider value={{}}>{props.children}</ChartContext.Provider>;
};
const Chart = () => {
return (
<SafeAreaView
style={{
flex: 1,
width: '100%',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.4,
shadowRadius: 6,
}}
>
<WebView
javaScriptEnabled={true}
startInLoadingState={true}
scrollEnabled={false}
injectedJavaScript={`const meta = document.createElement('meta'); meta.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no'); meta.setAttribute('name', 'viewport'); document.getElementsByTagName('head')[0].appendChild(meta);`}
onMessage={() => {}}
originWhitelist={['*']}
source={require('../../assets/index.html')}
/>
</SafeAreaView>
);
};
export { Chart, ChartProvider };
We also need to export the component. I decided to do this in a separate ‘index.tsx’ file:
import { Chart, ChartProvider } from "./Chart";
const ChartComponent = () => {
return (
<ChartProvider>
<Chart />
</ChartProvider>
);
};
export default ChartComponent;
This should be enough for us to have it part of your React Native app. All of the other necessary files are included in my Github project, which I will link down below. Let’s now see how our app looks in a simulator:
This is it!
Conclusions
In the ‘react-native-webview’ GitHub repository, there are a few known issues that users have encountered while trying to render content in the React Native WebView component. These issues might include difficulties in loading external resources or ensuring consistent rendering across different devices.
However, I managed to address these challenges by implementing a solution using a static HTML file. By encapsulating all necessary JavaScript and CSS within the HTML file itself, I eliminated the need for external dependencies and mitigated potential compatibility issues. This approach ensured a consistent user experience across various devices and platforms.
Incorporating a static HTML file proved to be an effective workaround for the known issues present in the GitHub repository, enabling me to achieve the desired functionality with minimal effort and ensuring a smooth and reliable application experience for users. You can find exactly how I did this in my repository. Happy coding! 🤙🏻