You already built the web app — now what?

Every year, clients ask the same question: “Can we turn this into a mobile app without rebuilding everything?”

The honest answer in 2026 is often yes, with Capacitor — if your web app is already React/Vite (or Vue/Angular) and your mobile requirements are standard: push notifications, offline storage, camera, share sheet, app store presence.

This guide documents the workflow I use across this site’s ecosystem — NetQuest (Vite submodule), Nepali Calendar, and freelance Capacitor deliveries — not theoretical tutorial content. For commercializing Capacitor boilerplates, see our separate licensing React Capacitor boilerplates analysis.

Capacitor vs alternatives (decision table)

Approach Code reuse App store Dev effort Best when
PWA only 100% web No (Add to Home Screen) Lowest Internal tools, no store requirement
Capacitor ~95% web Yes Medium Existing web app, standard native APIs
React Native ~60–80% logic Yes High Mobile-first UX, native feel required
Flutter 0% (Dart) Yes High Greenfield, custom UI performance

Capacitor’s niche: web developers shipping to app stores without learning Swift/Kotlin UI.

Capacitor web to mobile app development guide

Image: Arjan KC media library — React Capacitor boilerplates.

Our monorepo pattern (see BUILD_REFERENCE.md and GitHub Actions CI/CD post) builds Vite sub-apps into dist/netquest/, dist/nepali-calendar/, etc. Capacitor adds a parallel native shell pointing at that built web assets directory.

Project setup (Vite + React + Capacitor 6)

Starting from a working Vite app:

npm install @capacitor/core @capacitor/cli
npx cap init "App Name" com.yourcompany.appid
npm install @capacitor/android @capacitor/ios

Configure capacitor.config.ts:

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.arjankc.netquest',
  appName: 'NetQuest',
  webDir: 'dist',
  server: {
    // androidScheme: 'https' // recommended for secure context APIs
  },
};

export default config;

Build web assets first, then sync to native projects:

npm run build
npx cap sync
npx cap open android   # or ios

Critical rule: Always build → cap sync before testing native. Editing www/ manually gets overwritten.

Native plugins we actually use

Plugin Use case Notes
@capacitor/preferences Persist settings offline Replaces localStorage edge cases on iOS
@capacitor/push-notifications Re-engagement Requires FCM + APNs setup
@capacitor/share Share results/links NetQuest score sharing pattern
@capacitor/status-bar Brand color on launch Small polish, big perceived quality
@capacitor/splash-screen Launch experience Keep duration short
@capacitor/app Back button + deep links Android hardware back handling

Install per plugin, then npx cap sync. Do not bundle plugins you do not call — each adds native dependency surface.

Lessons from NetQuest and Nepali Calendar

1. Treat mobile as a viewport, not a separate product

Both apps share web codebase with responsive breakpoints. Capacitor does not remove the need for mobile UX:

  • Touch targets ≥ 44px
  • Bottom navigation for thumb reach
  • Avoid hover-only interactions
  • Test on mid-range Android (not just flagship iPhone)

2. Offline-first for utility apps

Calendar and quiz apps get opened in low-connectivity contexts (trekking areas, commute). Cache static data locally:

  • Ship JSON assets in bundle for calendar meta
  • Use @capacitor/preferences for user settings
  • Show stale-data indicator rather than blank screen

3. App Store review surprises

Common rejection reasons we have hit or seen:

Issue Prevention
Broken login on review device Provide demo account in review notes
Missing privacy policy URL Host policy on HTTPS page
WebView wrapper with minimal value Add push, offline, or native navigation
Placeholder content Production-ready assets before submission
IAP without StoreKit/RevenueCat Use official billing APIs

Build in 2–3 weeks of review buffer for first submission.

4. Deep linking and URL schemes

Web routes (/quiz/, /calendar/2082/) should map to app routes. Configure:

  • Android App Links (assetlinks.json)
  • iOS Universal Links (apple-app-site-association)
  • Custom URL scheme fallback

Our static site hosts association files at well-known paths — same pattern as Cloudflare routing.

Development workflow

┌─────────────┐     npm run build      ┌──────────┐
│  Vite/React │ ─────────────────────► │   dist/  │
│   source    │                        └────┬─────┘
└─────────────┘                             │ npx cap sync
                                            ▼
                              ┌─────────────────────────┐
                              │ android/ ios/ projects  │
                              │ (Android Studio / Xcode)│
                              └─────────────────────────┘

Daily loop:

  1. Develop in browser (npm run dev) — fastest iteration
  2. Build + sync before native-specific testing
  3. Test on real device weekly (simulators miss WebView quirks)
  4. CI: separate job for cap sync + gradle assemble (Android) — iOS needs macOS runner

This site’s npm run build:full includes Vite submodules; mobile builds typically run a scoped submodule build first.

Performance expectations

Capacitor apps are WebView-based. Expect:

  • Startup: 1–3s cold start depending on bundle size
  • Animations: 60fps achievable with CSS transforms; heavy JS main thread blocks UI
  • Memory: Large DOM hurts more than on desktop Chrome

Mitigations that worked for us:

  • Code-split routes (Vite dynamic import)
  • Tree-shake unused icon libraries
  • Avoid massive chart libraries on mobile views
  • Use will-change sparingly for animations

Compare against Core Web Vitals patterns — same LCP/INP thinking applies inside WebView.

OTA updates without app store delay

Capacitor allows live updates to web assets (not native code changes) via:

  • @capacitor/live-update (Ionic Appflow)
  • Custom OTA server (self-hosted bundle zip)

Caution: Apple restricts OTA that materially changes app purpose. Bug fixes and content updates are generally fine; turning a calendar app into a gambling app via OTA is not.

For Nepal-based teams, self-hosted OTA on Cloudflare R2 + worker is cost-effective vs Appflow subscription.

Security checklist

  • HTTPS only (no mixed content)
  • Certificate pinning for high-security apps (optional, adds complexity)
  • Secure storage for tokens (@capacitor/preferences vs plain localStorage)
  • Obfuscate API keys — nothing secret in client bundle
  • Validate deep link parameters server-side

Cost model (realistic)

Line item One-time Recurring
Apple Developer Program $99/year
Google Play Console $25
Push (FCM free, APNs via Apple)
macOS hardware (iOS builds) $800+
CI macOS minutes Variable
Developer time 40–120 hrs first ship 5–15 hrs/release

Capacitor saves hundreds of hours vs native rewrite — but app store logistics still cost real money and time.

When to graduate off Capacitor

Signals to migrate toward React Native or native:

  • App Store feedback cites “web app feel”
  • Performance bottlenecks in WebView profiling
  • Need for complex background tasks, Bluetooth, AR
  • Native UI components required by platform HIG

Most business utility apps never hit this ceiling.