Frameworks & existing projects

koru packages static files: an index.html and the scripts, styles and images it uses. Any framework that can export a static site works. Point webDir at the build output and koru does the rest.

On this page

Start from a framework template

The quickest path is a Vite-based template. They all need Node.js:

koru create my-app --template react
cd my-app
npm install
npm run dev        # Vite with hot reload
npm run android    # vite build, then koru run
npm run apk        # vite build, then a signed release APK
--template You get
react Vite with React 18 and JSX.
vue Vite with Vue 3 single-file components.
svelte Vite with Svelte 5.
vanilla-ts Vite with strict TypeScript and no framework.
vite A plain JavaScript Vite app, for any other setup.

Each template has a koru.json with "webDir": "dist" and a copy of the koru JavaScript module in src/koru/, so imports work in Vite’s dev server and in production builds. For Solid or another framework, start from vite and add it the usual way, for example npm install solid-js vite-plugin-solid and a vite.config.js.

These templates were built with npm run build, koru build and koru verify (plus tsc --noEmit for vanilla-ts). They have not been installed or run on Android, so test one on a device before you build on it.

Add koru to an existing project

From your project’s root, create a koru.json and signing key in place:

cd your-web-app
koru init

init also adds .koru/ to your .gitignore. Then edit koru.json: set name, choose your own appId and point webDir at your build output. Delete the sample www/ folder that init created if you do not use it.

{
  "name": "My App",
  "appId": "com.example.myapp",
  "webDir": "dist",
  "spa": true
}

These are the fields to change; keep the others init wrote. Each build is then two steps:

npm run build
koru run           # or: koru build --release --output my-app.apk

Output folders by framework

Framework Static build webDir Client-side routes?
Vite (React, Vue, Svelte, Solid) vite build dist Set "spa": true if you use a router
Create React App react-scripts build build "spa": true with React Router
Angular ng build dist/<project>/browser "spa": true
Next.js output: "export" in next.config.js, then next build out Leave off; pages are exported as HTML
Nuxt nuxi generate .output/public Leave off for generated routes
SvelteKit @sveltejs/adapter-static, then vite build build Use the adapter’s fallback: "index.html" and "spa": true
Astro astro build (static output, the default) dist Leave off

Server-side rendering, API routes and image optimization services do not run inside the APK. Use their static export, and call your server as an HTTPS API.

Routing and paths

With "spa": true, any path without a file extension (such as /settings/profile) returns index.html, so your router can choose the screen. Missing files with extensions, such as /assets/app.js, still return 404 so a typo does not load HTML as JavaScript.

Files are served from the root of https://app.koru.invalid/. Build with a base path of / (the default in most tools). If your site is configured for a sub-path such as /my-repo/, set the build’s base/publicPath back to / for the APK.

Use the JavaScript API from a bundler

Bundlers resolve imports at build time, so they cannot import /__koru/core.mjs, which only exists inside the app. Write a copy of the module into your sources:

koru js src/koru            # after upgrading koru: koru js src/koru --force

Then import { device, storage } from "./koru/core.mjs". In the APK it uses the native bridge; in a browser or vite dev it uses web implementations. core.d.mts next to it gives TypeScript and editors the types. For apps without a bundler, import /__koru/core.mjs directly, as the basic template does.

Call your API

Your app’s origin is https://app.koru.invalid. To call an HTTPS service, add the Internet permission and allow that origin in your server’s CORS settings:

"permissions": ["android.permission.INTERNET"]

Bundle your JavaScript dependencies: remote scripts and iframes are blocked by the app’s content security policy. Plain http:// requests are blocked too.

Keep hot reload while you work

Run your framework’s dev server and put koru dev in front of it to get the koru API with your real app details and plugin checks:

npm run dev
koru dev --proxy http://localhost:5173

See Develop with live reload. Then run on a device and distribute your app.