Hire Lovable Xperts
Mobile

Wrap a Lovable App With Capacitor

Capacitor is the fastest way to get a Lovable web app into the App Store and Google Play without rewriting a single screen. It packages your compiled Vite build — the exact HTML, CSS, and JavaScript Lovable generates — inside a native iOS and Android shell. This guide covers the setup, the native plugin integrations, Supabase deep-link auth, and the submission checklist.

By Founder Name · Last verified: 2026-06-24

What exactly does Capacitor do to a Lovable app?

Capacitor bundles your compiled web build — the `dist/` folder produced by `npm run build` — into a native iOS and Android container. That container renders your web app inside a native WebView, exposes device APIs (camera, push notifications, filesystem) via a JavaScript plugin bridge, and produces a signed binary that the App Store and Google Play accept. Your Lovable source code, Supabase configuration, and routing all remain unchanged.

The result is a real native app from the stores' perspective. Users download it from the App Store or Google Play, find it on their home screen, and can grant it permission to access the camera, location, and notifications. The UI renders your existing Lovable components — there is nothing to rewrite.

The practical constraint: anything that worked in a desktop Chrome tab works in Capacitor's WebView with minimal changes. Things that require OS-level background access — background location polling, background audio, persistent background tasks — require explicit Capacitor plugins and user permissions. Those are additions, not rewrites.

How do I install Capacitor in a Lovable project?

Capacitor is installed as an npm package directly into your Lovable project. Because Lovable generates a standard Vite + React project, the installation follows the same steps as any Vite app. The one Lovable-specific consideration is that your `vite.config.ts` build output directory must be `dist/` — Capacitor reads from there by default.

  1. Clone your Lovable project to a local machine using the GitHub integration (Lovable editor → GitHub → Export).
  2. Run `npm install @capacitor/core @capacitor/cli` to add Capacitor.
  3. Run `npx cap init` — enter your App Name and Bundle ID (e.g. com.yourcompany.appname). Use reverse-domain notation; this cannot easily change after App Store submission.
  4. Confirm `capacitor.config.ts` sets `webDir: 'dist'` to match your Vite output directory.
  5. Run `npm run build` to generate the `dist/` folder.
  6. Run `npx cap add ios` to scaffold the native iOS project (requires macOS + Xcode).
  7. Run `npx cap add android` to scaffold the native Android project.
  8. Run `npx cap sync` to copy the web build and any installed plugins into both native projects.
  9. Open Xcode: `npx cap open ios` — set your development team under Signing & Capabilities.
  10. Open Android Studio: `npx cap open android` — connect a device or emulator and run the app.

Which Capacitor plugins does a typical Lovable app need?

Most Lovable apps need three plugins at minimum: Push Notifications (for FCM on Android and APNs on iOS), Status Bar (to control the native status bar appearance), and possibly Camera if the app handles file uploads. Installing plugins requires adding the npm package, running `npx cap sync`, and then configuring any native permissions in `Info.plist` (iOS) or `AndroidManifest.xml` (Android).

Push Notifications setup: `npm install @capacitor/push-notifications && npx cap sync`. On iOS, add the Push Notifications capability in Xcode → Signing & Capabilities. On Android, add your `google-services.json` file to the `android/app/` directory. The plugin fires JavaScript events when a notification arrives; your app code stores the device token in Supabase.

Camera plugin: `npm install @capacitor/camera && npx cap sync`. Declare `NSCameraUsageDescription` in `ios/App/App/Info.plist` and `CAMERA` permission in `AndroidManifest.xml`. The Capacitor Camera API replaces the HTML `<input type='file' accept='image/*'>` for native camera access.

How do I configure Supabase auth deep links in Capacitor?

Supabase's OAuth and magic-link flows redirect to a URL after authentication. In a browser, that redirect returns to your web app. In a Capacitor app, you need the redirect to open your native app instead — via a custom URL scheme registered in the OS. Without this, your users get stuck in a browser tab after OAuth and the Capacitor app never receives the auth session.

  1. In `capacitor.config.ts`, add the scheme under the app section: `server: { androidScheme: 'https' }` and define a custom scheme for deep links.
  2. Register your scheme in `ios/App/App/Info.plist` under `CFBundleURLTypes` with the key `CFBundleURLSchemes` set to your bundle ID or custom scheme.
  3. Register the intent filter in `android/app/src/main/AndroidManifest.xml` under the main Activity.
  4. In your Supabase Auth settings (Dashboard → Authentication → URL Configuration), add your scheme as an allowed redirect URL: e.g. `com.yourcompany.appname://auth/callback`.
  5. In your Lovable app code, listen for the `appUrlOpen` Capacitor event and pass the received URL to `supabase.auth.setSession()` to complete the OAuth exchange.
  6. Test by triggering a Google OAuth sign-in and confirming the app opens and the user session is established.
Never set `https://` redirect URLs that point to your web domain as the Capacitor deep-link target. If the redirect goes to a URL, the OS opens the browser, not your app. The Capacitor app must receive a custom scheme (`myapp://`) URL that only your installed app is registered to handle.

What CSS changes does a Lovable app need for mobile?

Lovable apps designed for desktop browsers often have layouts that need minor adjustments for iOS and Android. The most important is the safe-area inset — on iPhones with a notch or Dynamic Island, content can be hidden behind the status bar or home indicator unless the CSS accounts for it. This is a 10-minute fix, not a redesign.

Add this to your global CSS to account for iOS safe areas: `padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);`. Set this on your root layout element, not on every individual component.

Remove any `position: fixed` elements that relied on `100vh` equaling the visible viewport — on iOS WebView, `100vh` includes the area behind the address bar. Use `100dvh` (dynamic viewport height) instead. Also verify that any bottom navigation bars clear the home indicator area on iPhone X and later models.

How do I submit the wrapped app to the App Store and Google Play?

Submission requires an Apple Developer account ($99/year) for the App Store, and a Google Play developer account ($25 one-time) for Google Play. Both stores require a signed binary. iOS signing happens in Xcode using your provisioning profile and distribution certificate. Android signing uses a keystore file you must keep securely — it cannot be recovered if lost.

  1. Apple: Create an App ID in the Apple Developer portal matching your bundle ID. In Xcode → Product → Archive, build a release archive. Use the Distribute App flow to upload directly to App Store Connect.
  2. In App Store Connect, create a new app record, fill in metadata (name, description, screenshots), and select the uploaded build for review.
  3. Google: In Android Studio → Build → Generate Signed Bundle/APK, create or select your keystore and build a release AAB.
  4. Upload the AAB to Google Play Console → Create app → Production track.
  5. Both stores require screenshots at required resolutions — iOS needs 6.5-inch and 5.5-inch screenshots at minimum; Google Play requires phone screenshots plus tablet if you target tablets.
  6. Submit for review. App Store review typically takes 1–3 business days for first submissions. Google Play review takes 1–7 days.

Frequently asked questions

Do I need to clone my Lovable project to use Capacitor?
Yes. Capacitor must be installed as a local npm package and requires you to run build commands from a terminal. Export your Lovable project to GitHub using the editor's GitHub integration, clone it locally, and run the Capacitor setup from there. Future updates follow the same pattern: pull from GitHub, build, cap sync, and re-submit.
Can Capacitor access the iPhone camera from a Lovable app?
Yes, using the `@capacitor/camera` plugin. Install it, sync it into the native project, declare the camera permission in Info.plist and AndroidManifest.xml, and call `Camera.getPhoto()` from your JavaScript. This replaces the basic HTML file input with a native camera picker that also allows choosing from the photo library.
How often do I need to resubmit to the App Store after a Lovable update?
Every time you change JavaScript, CSS, or assets you technically need a new binary submission — Apple's guidelines require that app binaries contain the final UI. In practice, Capacitor's live-update capability (and Ionic's Appflow) can push JS-only changes without a resubmission for minor updates, but this operates in a gray area of Apple's policy. For significant UI changes, submit a new binary.
What is the difference between npx cap sync and npx cap copy?
`cap sync` copies your web build into native projects AND updates native plugins. `cap copy` only copies web assets. Use `cap sync` after installing or updating any Capacitor plugin. Use `cap copy` for pure web content updates when you have not changed any plugins — it is faster.
Will my Supabase real-time subscriptions work in the Capacitor WebView?
Yes. Supabase real-time uses WebSockets, which Capacitor's WebView supports. The connection will drop when the app moves to the background on iOS (which aggressively terminates WebSocket connections). You need to reconnect on the `appStateChange` resume event. The Supabase JS client handles reconnection automatically if you call `supabase.realtime.connect()` on resume.
How do I handle push notifications in my Lovable app after wrapping it?
Install `@capacitor/push-notifications`, run `npx cap sync`, and add the Push Notifications capability in Xcode. On first app launch, call `PushNotifications.requestPermissions()` and then `PushNotifications.register()`. Listen for the `registration` event to get the device token, then store that token in your Supabase database linked to the user ID. Use a Supabase Edge Function or a backend service like Firebase Functions to send notifications to the stored token.
Can I test the Capacitor app without a physical device?
Yes — both iOS Simulator (Xcode) and Android Emulator (Android Studio) run Capacitor apps. The Simulator is useful for layout testing but does not support real push notifications (you need a physical device for APNs tokens). Android emulators support FCM push notifications. For app-store submission, Apple requires testing on a real device before submission review.
What if my Lovable app uses iframes or external scripts that Capacitor blocks?
Capacitor's WebView enforces a Content Security Policy that blocks iframes loading external origins and dynamically injected external scripts. Audit your Lovable app for any embedded iframes (Stripe payment elements, YouTube embeds, third-party widgets) and configure the CSP in `capacitor.config.ts` to explicitly allow those origins. Stripe's mobile integration requires their mobile SDK, not an iframe, for the best experience.

Talk to a senior engineer — not a salesperson.

Book a free 30-minute audit call. We'll diagnose what's wrong and tell you exactly what it costs to fix.

Book a free audit call