JavaScript stops working inside an APK for one underlying reason: the page is no longer served from an https:// origin but loaded from the app's local files, and browsers apply stricter rules to local files. Module scripts are refused, fetch() of a local file is refused, root-relative paths resolve to the wrong place, and anything loaded from a CDN needs a connection the phone may not have. Below are the ten causes that account for nearly every "works in Chrome, blank in the app" report, in the order to check them, each with a test and a fix.
First, the two-minute test. Double-click your index.html so Chrome opens it from disk (file:///…), open DevTools (F12) and look at the Console. Red errors here are the same errors the app hits. Reproduce, fix, reload — no rebuild needed until it is clean.
1. type="module" scripts
Symptom: blank page; console says Access to script … from origin 'null' has been blocked by CORS policy. Cause: module scripts are always fetched with CORS, and a local file has no origin to satisfy it. Fix: remove type="module", remove import/export statements, and load dependencies with ordinary <script> tags in order. If the code is a Vite or similar build, set the build target to produce a classic IIFE bundle (Vite: build.rollupOptions.output.format = 'iife', or use the legacy plugin).
2. Libraries from a CDN
Symptom: works on Wi-Fi, breaks on the train; console says $ is not defined or Failed to load resource. Fix: download the library file into your folder and reference it with a relative path. Do the same for CSS frameworks and icon fonts.
3. Paths that start with /
Symptom: 404s for your own files; unstyled page; images missing. Cause: /js/app.js means the device root, not your folder. Fix: js/app.js. Check href, src, CSS url() and strings your JavaScript builds into URLs.
4. fetch() or XMLHttpRequest for a local file
Symptom: Failed to fetch or a CORS error when loading data.json, a template, or a text file that sits right next to the page. Cause: local-file requests from a local page are blocked. Fix: inline the data as a JavaScript object (const DATA = {…}) or in a <script type="application/json"> tag you parse with JSON.parse(el.textContent). Requests to a real https:// API still work when online.
5. Case-sensitive file names
Symptom: one image or script missing in the app, fine on your computer. Cause: Hero.JPG on disk, hero.jpg in the HTML — Windows and macOS forgive it, Android does not. Fix: lower-case everything.
6. Missing viewport tag
Symptom: the page works but is tiny, laid out for a desktop and shrunk. Fix: <meta name="viewport" content="width=device-width, initial-scale=1"> in the head.
7. Syntax too new for the phone's WebView
Symptom: works on your phone, blank on an older one; console says Unexpected token. Cause: the WebView updates through Google Play, but an old phone that has not updated may lack support for very recent syntax (the ?. operator arrived in 2020, top-level await and class fields later). Fix: avoid the newest syntax, or transpile with a target of a few years back. Android 7 and up covers the vast majority of active devices; test on the oldest one you care about.
8. A service worker registration that throws
Symptom: an uncaught error at startup that stops the rest of the script. Cause: navigator.serviceWorker.register() fails from a local page. Fix: wrap it — if ('serviceWorker' in navigator && location.protocol.startsWith('http')) { … } — or remove it; the bundle is already offline.
9. Popups and target="_blank"
Symptom: a link or window.open() does nothing. Cause: a WebView has one window; there is nowhere for a second to open. Fix: for external sites, a plain link — the builder opens other hosts in the phone's browser. For your own pages, navigate in the same window.
10. alert() during load, or a script before the elements exist
Symptom: Cannot read properties of null; buttons that do nothing. Cause: the script runs in the head before the body is parsed — the same bug as on the web, but easier to miss when there is no Network tab slowing things down. Fix: move the script to the end of the body, or wrap it in DOMContentLoaded. (alert() itself works, it just looks like a browser.)
Still blank?
- Confirm
index.htmlis at the root of the ZIP, not inside a folder. - Confirm the file is under the size limit for your tier (5 MB free, 18 MB Pro) — a rejected upload is not a blank app, but a truncated one can be.
- Connect the phone by USB, open
chrome://inspecton your computer, and inspect the app's WebView directly. The console you see is the app's own. If the builder's debug option is on, this works on any build.