DEV Community

Giovani Fouz
Giovani Fouz

Posted on

WebVirt : Load your SPA in Android WebView with 3 lines of code

# πŸš€ WebVirt: Load your SPA in Android WebView with just 3 lines of code

Tired of writing 100+ lines of boilerplate just to display a SPA in a WebView?

**WebVirt reduces it to 3 lines.**

---

## 😩 The problem (we've all been there)

Packaging a SPA (React, Vue, Angular, Svelte, SolidJs...) into an Android WebView should be trivial… but it's not:

- Manually configuring `WebViewClient`
- Intercepting requests (`shouldInterceptRequest`)
- Resolving SPA routes (`/products/42` β†’ `index.html`)
- Handling MIME types correctly
- Implementing asset caching
- Preventing directory traversal attacks
- Controlling external traffic

All that… before you even start building your app.

---

## ✨ The solution: WebVirt

Enter fullscreen mode Exit fullscreen mode


java
WebVirt.with(this)
.host("myapp.local")
.bind(webView);

webView.loadUrl("https://myapp.local/");


That's it.

WebVirt turns your APK into an internal virtual web server.

Your SPA lives in /assets and is served exactly like it would be in production.

---

πŸ”’ Offline-first security by default

WebVirt follows a simple rule:

If you don't explicitly allow it, it's blocked.

· 🚫 External traffic blocked by default
Β· βœ… Whitelist with allowExternalDomains()
Β· πŸ”Œ Offline mode with offlineOnly(true)
Β· πŸ›‘ Directory traversal protection

πŸ“¦ Automatic HTTP headers

Enter fullscreen mode Exit fullscreen mode


java
WebVirt.with(this)
.host("myapp.local")
.allowExternalDomains("api.myapp.com", "cdn.myapp.com")
.bind(webView);


---

🧠 Automatic SPA routing

Using React Router, Vue Router, or Angular?

WebVirt detects extensionless routes and serves index.html automatically:

Β· /about β†’ index.html βœ…
Β· /products/42 β†’ index.html βœ…
Β· /app.js β†’ app.js βœ…
Β· /style.css β†’ style.css βœ…

No configuration. No hacks. No 404s.

---

⚑ Smart in-memory caching

Optimized for real performance:

Β· 🧠 index.html β†’ memory-cached (never stale)
Β· πŸ“¦ Assets (JS, CSS, JSON, WASM, fonts) β†’ immutable cache (max-age=31536000)

Enter fullscreen mode Exit fullscreen mode


java
webVirt.clearCache(); // Ideal in onDestroy()


---

πŸ“¦ Installation

settings.gradle

Enter fullscreen mode Exit fullscreen mode


gradle
dependencyResolutionManagement {
repositories {
maven { url 'https://jitpack.io' }
}
}


build.gradle

Enter fullscreen mode Exit fullscreen mode


gradle
dependencies {
implementation 'com.github.fouzstack:fouzstack-webvirt:v1.0.0'
}


---

🎨 Advanced configuration (optional)

Enter fullscreen mode Exit fullscreen mode


java
WebVirt.with(this)
.host("myapp.local")
.subfolder("dist")
.allowExternalDomains("api.example.com")
.config(cfg -> {
cfg.setCacheEnabled(true);
cfg.setJavaScriptEnabled(true);
cfg.setDomStorageEnabled(true);
})
.bind(webView);


Full access to WebViewSettings without breaking the fluent API.

---

πŸ“Š Before vs After

Without WebVirt With WebVirt
~100 lines of code 3 lines
Manual WebViewClient Automatic
Broken SPA routing Works out-of-the-box
Manual caching Included
MIME type hell Included
Manual security Offline-first

---

🌐 Repository

πŸ‘‰ github.com/fouzstack/fouzstack-webvirt

---

πŸ€” Why "WebVirt"?

Because your APK becomes a:

Virtual web server without a real server

Β· No ports
Β· No network
Β· No real localhost

Just your SPA running like in production.

---

πŸ§ͺ Requirements

Β· Android 4.4+ (API 19)
Β· AndroidX
Β· Compile SDK 34+

---

πŸ“„ License

MIT β€” use it without fear.

---

πŸ’­ Final thought

The best tools aren't the biggest.

They're the ones you set up in seconds… and then forget.

WebVirt doesn't want the spotlight.
It wants you to focus on your product.

---

⭐ Did it help you?

Β· Star it on GitHub
Β· Open an issue if you find something
Β· PRs are welcome

Happy coding πŸš€

Enter fullscreen mode Exit fullscreen mode

Top comments (0)