Tutorial10 min read

Build iOS and Android apps from your SvelteKit app with Capacitor

Wrap a SvelteKit app as native iOS and Android with Capacitor using the static adapter, then wire up OtaKit so you can ship JS and CSS changes over the air in minutes.

SvelteKit apps are fast, small, and — with the static adapter — a perfect payload for Capacitor. You keep your routes, components, and stores, and ship the same codebase to the web, iOS, and Android. No rewrite, no separate mobile stack.

This guide takes a SvelteKit app to installable native builds, then adds OtaKit so you can push updates over the air rather than through a store review each time.

Mental model: the static adapter turns SvelteKit into a shippable web bundle; Capacitor wraps it natively; OtaKit keeps it updated after launch.

1. Switch to the static adapter

Install and configure adapter-static so SvelteKit prerenders a fully static site:

npm install -D @sveltejs/adapter-static
// svelte.config.js
import adapter from "@sveltejs/adapter-static";

export default {
  kit: {
    adapter: adapter({ fallback: "index.html" }),
    paths: { relative: true }, // relative paths for file:// context
  },
};

Enable prerendering in your root layout. The index.html fallback lets client-side routing handle any path inside the web view:

// src/routes/+layout.ts
export const prerender = true;
export const ssr = false;

Server endpoints (+server.ts) don't run on-device — call your API over HTTPS instead. Building now emits a static site to build/.

2. Add Capacitor

npm install @capacitor/core
npm install -D @capacitor/cli

npx cap init "My App" com.example.myapp --web-dir build

npm install @capacitor/ios @capacitor/android
// capacitor.config.ts
import type { CapacitorConfig } from "@capacitor/cli";

const config: CapacitorConfig = {
  appId: "com.example.myapp",
  appName: "My App",
  webDir: "build",
};

export default config;

3. Build the native apps

npm run build
npx cap add ios
npx cap add android
npx cap sync
// package.json
"scripts": {
  "mobile": "vite build && npx cap sync"
}

4. Reach device features

npm install @capacitor/haptics
npx cap sync
import { Haptics, ImpactStyle } from "@capacitor/haptics";

async function tap() {
  await Haptics.impact({ style: ImpactStyle.Light });
}

5. Add live updates with OtaKit

npm install @otakit/capacitor-updater
npx cap sync
// capacitor.config.ts
const config: CapacitorConfig = {
  appId: "com.example.myapp",
  appName: "My App",
  webDir: "build",
  plugins: {
    OtaKit: { appId: "YOUR_OTAKIT_APP_ID" },
  },
};

Confirm a successful boot from your root layout so OtaKit can auto-roll-back:

// src/routes/+layout.svelte
<script lang="ts">
  import { onMount } from "svelte";
  import { Capacitor } from "@capacitor/core";
  import { OtaKit } from "@otakit/capacitor-updater";

  onMount(() => {
    if (Capacitor.isNativePlatform()) OtaKit.notifyAppReady();
  });
</script>
npm install -g @otakit/cli
otakit login

npm run build
otakit upload --release

After your first store release with the plugin in place, every otakit upload --release lands on installed devices on the next launch. Bundles are signed, hash-verified, and CDN-delivered — with no monthly-active-user or bandwidth metering. Native changes still go through the store; your SvelteKit build ships over the air.

Want to roll out gradually? See staged rollouts for the channel-based approach.

Where to go next

The setup guide has the complete flow. Then read common OTA mistakes and OTA security.

Related docs