Phase 1

Prepare the web app

Get the code on your machine and native-ready

1–2 hours0/5 done

Capacitor bundles the finished output of your web build. Anything that only works on a live server — server-rendered routes, server functions, redirects to your own domain — needs a plan before you wrap. Fix this now; debugging it inside a native shell is far harder.

01

Move the project to your machine

Connect the project to GitHub from Lovable (top-right GitHub button → Connect), then clone it. Everything from here happens in your own repository — the native folders and signing configuration cannot be created from inside Lovable.

Clone and run
git clone https://github.com/YOUR-USER/my-app.git
cd my-app
npm install
npm run dev      # confirm it works at http://localhost:8080
02

Produce a static build the shell can load

The native shell loads files from disk, not from a Node server. Your app must build to a folder of static assets that works when opened from the filesystem with client-side routing.

  1. Run npm run build and confirm it completes without errors.
  2. Note the output folder — usually dist/ or dist/client/. That path becomes webDir in your Capacitor config.
  3. Serve the output locally and click through every route: npx serve dist (or your output folder).
  4. Check the browser console for 404s on assets. Absolute paths that assume a domain root are the usual culprit.
Use one base URL everywhere
// src/lib/api-base.ts
export const API_BASE =
  import.meta.env.VITE_API_BASE ?? "https://your-app.lovable.app";

export const api = (path: string) => `${API_BASE}${path}`;
03

Audit for things that break in a web view

PatternWhat happens nativelyFix
Relative fetch (/api/x)Resolves against capacitor://localhost and failsPrefix with your absolute API base URL
Cookies for authThird-party cookie rules block them in the web viewUse bearer tokens in native-safe storage
window.open to external sitesOpens inside your app or is blockedUse the Browser plugin to open the system browser
OAuth redirect to a web callbackRedirect never returns to the appUse a custom scheme / App Link callback
localStorage for critical dataCan be evicted by the OSUse the Preferences plugin
Hover-only interactionsNo hover on touch devicesGive every hover action a tap equivalent
Fixed 100vh layoutsClipped by status bar and home indicatorUse 100dvh plus safe-area insets
Downloads via <a download>Silently does nothingUse the Filesystem + Share plugins
04

Make the layout phone-first

Test at 360×640 (small Android) and 390×844 (iPhone) in your browser's device mode before you wrap. Tap targets should be at least 44×44 px, text at least 16 px in inputs (smaller text makes iOS zoom on focus).

Viewport tag — must include viewport-fit=cover
<meta
  name="viewport"
  content="width=device-width, initial-scale=1, viewport-fit=cover"
/>
Safe-area utilities
:root {
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}

.app-shell {
  min-height: 100dvh;
  padding-top: var(--safe-top);
  padding-bottom: var(--safe-bottom);
}
05

Write the privacy policy you will need

Both stores require a publicly reachable privacy policy URL before you can submit — even for an app that collects nothing. Publish it as a page on your web app now so the URL is stable.

  • What data you collect (accounts, analytics, crash logs, device identifiers).
  • Why you collect it and who you share it with (name each third party).
  • How long you keep it and how a user requests deletion.
  • A contact email that you actually monitor.
  • If your app has accounts, Play also requires an in-app account deletion path and a public web URL for deletion requests.