Loading Local HTML Files in Android WebView
An Android WebView can display HTML stored inside an application, allowing developers to package help pages, terms of service, product documentation, or an entire offline interface with an APK. This approach is useful when content must remain available without mobile data, such as a field app used outside Sydney, Melbourne, or regional Queensland.
The basic process involves adding HTML, CSS, JavaScript, images, and fonts to the assets directory, configuring the WebView, and loading the correct local URL. A careful setup also considers navigation, security, responsive layouts, and the difference between development files and content downloaded after installation.
Preparing The Local WebView Content
Create an assets folder in the Android module at app/src/main/assets. Store the main document there, for example help/index.html, with supporting files in nearby folders:
app/src/main/assets/
├── help/
│ ├── index.html
│ ├── css/style.css
│ ├── js/app.js
│ └── images/logo.png
Relative paths are important. If index.html contains <link rel="stylesheet" href="css/style.css">, the CSS file must be located beneath the same help directory. A path beginning with / often points somewhere unexpected in a local document, so relative references are generally safer.
A basic layout file can contain:
<WebView
android:id="@+id/helpWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Keep the page responsive by including a viewport declaration:
<meta name="viewport" content="width=device-width, initial-scale=1">
This helps the content fit smaller Android screens, including the phones commonly used by commuters on Melbourne trains or workers moving between sites in Perth.
Loading An Asset In Kotlin
For a simple, trusted document, Kotlin can load an asset directly:
val webView = findViewById<WebView>(R.id.helpWebView)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.loadUrl("file:///android_asset/help/index.html")
The file:///android_asset/ prefix is a special Android path. It does not refer to a normal file system location; instead, it maps to the application’s packaged assets. Use forward slashes and match the file name’s capitalisation exactly.
JavaScript should be enabled only when the page needs it. A static help page does not require JavaScript, and leaving it disabled reduces the available attack surface. When scripts are necessary, avoid exposing sensitive Android objects through JavaScript interfaces unless the content is fully controlled.
For a more modern implementation, WebViewAssetLoader serves local files through an HTTPS-style origin:
val assetLoader = WebViewAssetLoader.Builder()
.addPathHandler(
"/assets/",
WebViewAssetLoader.AssetsPathHandler(this)
)
.build()
webView.webViewClient = object : WebViewClientCompat() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
return assetLoader.shouldInterceptRequest(request.url)
}
}
webView.loadUrl("https://appassets.androidplatform.net/assets/help/index.html")
This method can make origin behaviour more predictable for CSS, JavaScript, and browser APIs. It is also a useful pattern to study alongside broader Android development notes when comparing local content approaches.
Choosing Between Asset And File Storage
Assets are read-only resources packaged into the application. They suit documentation that ships with every installation and rarely changes. If the app contains a safety guide for a work crew near the Pilbara, for instance, placing the guide in assets ensures it remains available when coverage drops out.
Files in internal storage are better when HTML is generated, downloaded, or updated. A news screen for an Australian community organisation might cache the latest notices after a user connects to Wi-Fi, then display the saved page during an outage. In that case, use filesDir or cacheDir, validate the downloaded content, and load only approved paths.
| Requirement | Suitable approach | Main consideration |
|---|---|---|
| Fixed help pages | assets with file:///android_asset/ |
Content changes require an app update |
| Local HTML with modern origin behaviour | WebViewAssetLoader |
Requires a WebViewClient configuration |
| Downloaded or generated pages | Internal app storage | Validate content and manage stale files |
| Remote pages | HTTPS URL | Requires network access and external trust |
| Interactive local interface | Assets plus JavaScript | Enable scripts only when needed |
Do not request INTERNET permission merely to load packaged assets. That permission is needed for network requests, not for HTML embedded in the APK. If the page loads external fonts, images, analytics, or APIs, those dependencies can make an otherwise offline screen fail in remote areas of Western Australia.
Handling Links, Navigation, And State
A local page may include links to another HTML file, a PDF, or an external website. Relative local links normally remain inside the WebView, while an absolute HTTPS link may need custom handling. A WebViewClient prevents Android from sending every navigation request to a separate browser:
webView.webViewClient = WebViewClient()
For controlled apps, override URL handling and allow only expected schemes such as https, mailto, or local app asset URLs. Reject unknown schemes rather than passing them directly to another application. This matters in apps used by councils, schools, and small businesses where content may come from several sources.
Back navigation also needs deliberate behaviour. A user who taps through several local pages usually expects the Android back button to return to the previous document:
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
super.onBackPressed()
}
}
If the same screen includes a searchable list of documents, a ListView search filter can sit beside the WebView and open the selected local page. Preserve the selected item and WebView history during rotation where practical.
Testing Performance And Security
Test the page on an emulator and a physical Android device. Check portrait and landscape orientations, dark mode, large font settings, broken image paths, and devices with limited memory. A page that looks fine in Chrome can still fail in WebView because of unsupported browser features or an incorrect asset URL.
Use Android Studio’s WebView inspection tools to review console errors and failed resource requests. If a stylesheet is missing, confirm the case-sensitive path and inspect the actual APK contents. Test with airplane mode enabled so the app’s offline behaviour is clear, particularly for users travelling through the Nullarbor or working in inland South Australia.
Keep local HTML trusted and minimise JavaScript bridges. Avoid loading untrusted HTML from an intent, query parameter, or remote download without sanitising it. Disable file access settings that the application does not need, keep external navigation restricted, and use HTTPS for every network resource. These safeguards let a packaged WebView remain useful without turning a documentation screen into an avoidable security risk.