Frameworks & games

React, Vue, Svelte or Vite build to APK

A framework's production build is just a folder of HTML, CSS and JavaScript — so it zips and converts like any other site, once three settings are right: a relative base path, a classic (non-module) script output, and hash-based routing. The exact config for each tool.

8 min read Updated September 2026

Quick answer: run the production build with a relative base path (base: './' in Vite, "homepage": "." in Create React App), make sure the output is a classic script rather than an ES module (Vite: @vitejs/plugin-legacy or an IIFE output), switch the router to hash mode, then zip the contents of dist/ (or build/) so index.html is at the root and upload it to the ZIP to APK builder.

Why the default build fails in an app

Bundlers assume a web server at a known root. Three assumptions leak into the output: asset URLs start with /, the entry script is loaded as type="module", and the router expects the server to return index.html for any path. Inside an APK there is no server and the page has no origin, so each assumption breaks — the blank-screen guide explains the symptoms. Each has a one-line config fix.

Vite (React, Vue, Svelte, Solid, vanilla)

// vite.config.js
import { defineConfig } from 'vite';
import legacy from '@vitejs/plugin-legacy';   // npm i -D @vitejs/plugin-legacy

export default defineConfig({
  base: './',                                  // relative asset paths
  plugins: [
    legacy({ renderLegacyChunks: true, modernPolyfills: false }),
  ],
  build: { outDir: 'dist' },
});

@vitejs/plugin-legacy emits a classic <script nomodule> fallback alongside the module build; on a file:// page the module is blocked and the fallback runs. If you would rather not ship both, force a single classic bundle instead:

build: {
  rollupOptions: { output: { format: 'iife', inlineDynamicImports: true } },
  modulePreload: false,
}

…and change the <script type="module"> in the generated index.html to a plain <script> (a small post-build step, or use the vite-plugin-singlefile package which inlines everything into one HTML file — ideal for the single-file route).

Create React App

// package.json
"homepage": "."

CRA already outputs classic scripts. Zip the contents of build/.

Vue CLI

// vue.config.js
module.exports = { publicPath: './' };

Angular

ng build --base-href ./ --configuration production

Angular 17+ emits ES modules by default. Set "outputMode"/"target" so the browser build includes a classic fallback, or serve through the legacy builder; and use provideRouter(routes, withHashLocation()).

Next.js (static export only)

// next.config.js
module.exports = { output: 'export', assetPrefix: './', trailingSlash: true, images: { unoptimized: true } };

Zip the out/ folder. Anything that needs a server — API routes, server components, ISR, middleware — is unavailable.

Routing: use hash mode

RouterChange
React RouterBrowserRouter → HashRouter
Vue RoutercreateWebHistory() → createWebHashHistory()
AngularwithHashLocation() / useHash: true
SvelteKitStatic adapter with router: { type: 'hash' } (SvelteKit 2.x) or a client-only fallback page

URLs become index.html#/about. Nothing else in your app changes.

Data and APIs

Requests to a real https:// API work when the phone is online, with CORS evaluated against Origin: null — if your API whitelists origins, allow null or use a wildcard. Local JSON in the bundle must not be fetched (see what works offline); import it into the bundle at build time instead — import data from './data.json' — so it becomes part of the JavaScript.

Then zip and convert

  1. Build: npm run build.
  2. Open dist/index.html from disk in Chrome. If it renders and the console is clean, you are done with config.
  3. Zip the contents of dist/ — the Folder to ZIP tool does it correctly and skips source maps.
  4. Upload to the builder, set name, icon, package name, version, build.
ZIP to APK CheckerValidate your ZIP before you convert it Open the tool

Checklist

Questions people ask

Why does my Vite build show a blank screen in the app?

Vite emits