---
title: Build Mobile Apps with Any Web Framework and Capacitor
description: Turn React, Angular, Vue, Svelte, or any web app into native iOS and Android apps with Capacitor — setup, configuration, live reload, and release tips.
date:
  created: 2026-05-11
  updated: 2026-07-17
authors:
  - djabif
categories:
  - Capacitor
  - Guides
faq: true
---

# Build Mobile Apps with Any Web Framework and Capacitor

If your team already ships a modern web app with React, Angular, Vue, Svelte, or another framework, you don't need to start from scratch to reach the iOS and Android stores. Capacitor lets you wrap your existing frontend code in native projects and add mobile capabilities where you actually need them.

This guide walks through the full workflow across frameworks, from setup and native platform generation to Xcode and Android Studio integration, live reload, and release automation. We also cover common pitfalls, how to add native functionality with plugins, and where Capawesome Cloud fits when you want to automate builds and deployment.

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="https://capawesome.io/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

## Why Capacitor Works with Any Web Framework

Capacitor is an open-source runtime developed by the Ionic team that runs your web app as a native iOS and Android app. It packages your built web assets into a native shell and renders them inside a `WebView`, while a thin native bridge exposes device APIs to your JavaScript code through plugins.

Because Capacitor only cares about your final build output, the framework you use to produce that output is mostly irrelevant. Whether you're building with React, Angular, Vue, Svelte, or another JavaScript framework, the core workflow stays almost identical. In practice, the biggest differences are your build command and the output directory you point Capacitor at.

## Shared Setup Flow for Any Framework

Start from your existing web project root.

### 1. Install Capacitor

```bash
npm install @capacitor/core@latest
npm install -D @capacitor/cli@latest
```

### 2. Initialize Capacitor

```bash
npx cap init
```

The CLI prompts you for your app name, app ID (e.g. `io.company.app`), and web output directory (`dist`, `build`, `www`, `.output/public`, and so on). See [Framework-Specific Configuration](#framework-specific-configuration) below for typical values per framework.

### 3. Add Native Platforms

```bash
npm install @capacitor/ios@latest @capacitor/android@latest
npx cap add ios
npx cap add android
```

### 4. Build Web Assets and Sync

```bash
npm run build
npx cap sync
```

### 5. Open Native Projects

```bash
npx cap open ios
npx cap open android
```

This becomes your core dev loop: `build -> sync -> open/run`.

## Framework-Specific Configuration

Most of the workflow is identical across frameworks. The main difference is the `webDir` value in your [Capacitor configuration](https://capacitorjs.com/docs/config){:target="_blank"} file, which must match your framework's build output directory. Here's a typical `capacitor.config.ts` for each framework:

=== "Angular"

    ```typescript title="capacitor.config.ts"
    import { CapacitorConfig } from "@capacitor/cli";

    const config: CapacitorConfig = {
      appId: "io.company.app",
      appName: "Example App",
      webDir: "www"
    };

    export default config;
    ```

    Use `www` if you set `outputPath` accordingly in `angular.json`, or `dist/<project-name>/browser` for Angular's default build output.

=== "React (Vite)"

    ```typescript title="capacitor.config.ts"
    import { CapacitorConfig } from "@capacitor/cli";

    const config: CapacitorConfig = {
      appId: "io.company.app",
      appName: "Example App",
      webDir: "dist"
    };

    export default config;
    ```

    For Create React App projects, use `webDir: "build"` instead.

=== "Vue (Vite)"

    ```typescript title="capacitor.config.ts"
    import { CapacitorConfig } from "@capacitor/cli";

    const config: CapacitorConfig = {
      appId: "io.company.app",
      appName: "Example App",
      webDir: "dist"
    };

    export default config;
    ```

=== "SvelteKit"

    ```typescript title="capacitor.config.ts"
    import { CapacitorConfig } from "@capacitor/cli";

    const config: CapacitorConfig = {
      appId: "io.company.app",
      appName: "Example App",
      webDir: "build"
    };

    export default config;
    ```

    Use `@sveltejs/adapter-static` so the build output is a fully static directory that Capacitor can package.

If you prefer JSON, you can use `capacitor.config.json` with the same fields. If `webDir` doesn't match your real build output, you'll see a blank screen on launch — see [Common Pitfalls](#common-pitfalls-and-fixes) below.

## Connecting Your Project to Xcode and Android Studio

After `cap add`, Capacitor creates two native projects:

- `ios/` for Xcode
- `android/` for Android Studio

Your frontend stays the source of truth for UI and business logic. The native projects handle permissions, signing, native configuration, and app store packaging.

A practical daily workflow looks like this:

1. Change web code.
2. Run `npm run build`.
3. Run `npx cap sync`.
4. Run and debug from Xcode or Android Studio.

When native settings change (permissions, Gradle config, Xcode signing), edit the native project directly. When web assets change, always rebuild and sync to avoid platform drift.

## Live Reload Workflow During Development

To speed up feedback during development, you can use live reload on a real device:

```bash
npx cap run android -l --external
npx cap run ios -l --external
```

A few prerequisites and gotchas:

- Start your framework's dev server first (`npm run start`, `npm run dev`, etc.) and make sure your computer and device are on the same network.
- `--external` binds the dev server to all network interfaces, so allow incoming connections on the dev server port if your firewall asks.
- Live reload is a development workflow, not a production deployment strategy — disable it before shipping a real build.

## App Icons and Splash Screens

Instead of manually resizing assets, generate platform assets with the official tool:

```bash
npm install -D @capacitor/assets@latest
npx capacitor-assets generate --ios
npx capacitor-assets generate --android
```

Recommended workflow:

1. Keep your icon and splash screen original files in a `resources/` directory.
2. Generate iOS and Android assets from those sources.

Tool reference: [@capacitor/assets](https://github.com/ionic-team/capacitor-assets){:target="_blank"}.

## Adding Native Functionality with Plugins

Use plugins when your app needs device features that a browser alone can't provide reliably.

In many cases, native plugins are also the more performant option. Browser APIs are powerful, but plugins can use native execution paths and platform-specific capabilities that are better suited for heavy mobile workloads.

A few examples:

- Capture photos and pick from the gallery with the [Camera](https://capacitorjs.com/docs/apis/camera){:target="_blank"} plugin.
- Build typical attach-and-upload flows with the [Capacitor File Picker plugin](../../sdks/capacitor/file-picker.md) and [Capacitor File Compressor plugin](../../sdks/capacitor/file-compressor.md).
- Add secure authentication with the [Capacitor OAuth plugin](../../sdks/capacitor/oauth.md) and [Capacitor Biometrics plugin](../../sdks/capacitor/biometrics.md).
- Receive content from other apps with the [Capacitor Share Target plugin](../../sdks/capacitor/share-target.md), or send push notifications by following [The Push Notifications Guide for Capacitor](./capacitor-push-notifications-guide.md).

Plugin usage is intentionally minimal — call a method, get a typed result. Here's how taking a photo with the Camera plugin looks from your web code:

```typescript
import { Camera, CameraResultType } from "@capacitor/camera";

const takePhoto = async () => {
  const photo = await Camera.getPhoto({
    quality: 90,
    allowEditing: false,
    resultType: CameraResultType.Uri
  });

  return photo.webPath;
};
```

The returned `webPath` can be assigned directly to an `<img>` `src`, so the same call works whether the user took a fresh shot or picked an existing photo from their gallery.

## Live Updates: Benefits, Limits, and Best Use Cases

[Live Updates](../../cloud/live-updates/index.md) let you ship web-layer changes (HTML, CSS, JavaScript) without waiting for app store review on every bug fix. This shortens release cycles and improves response time for production issues.

But the limits matter:

- Live Updates don't replace native binary updates.
- Native changes (a new plugin, a native SDK update, an entitlement change) still require app store builds.

Use Live Updates for fast iteration on the web layer, and native releases for native code changes.

For deeper context, read [How Live Updates for Capacitor Work](./how-live-updates-for-capacitor-work.md) and [How Live Updates Are Changing Mobile App Deployment](./how-live-updates-are-changing-mobile-app-deployment.md).

## Native vs Web Changes Decision Matrix

Use this quick matrix to decide whether you should ship through Live Updates or create a new native store build:

| Change type                                 | Live Update | New native build |
| ------------------------------------------- | ----------- | ---------------- |
| CSS/UI text/layout fixes                    | Yes         | No               |
| Business logic in web layer                 | Yes         | No               |
| New route/page in frontend                  | Yes         | No               |
| Add or update Capacitor plugin              | No          | Yes              |
| iOS `Info.plist` or Android manifest change | No          | Yes              |
| New native SDK dependency                   | No          | Yes              |
| Entitlements, signing, push capabilities    | No          | Yes              |

If a change touches native code or native project configuration, plan for a store release. If it's only web assets, Live Updates is typically the faster path.

## Common Pitfalls and Fixes

Teams usually hit the same issues during the first week of integration. These are the most common ones and how to fix them quickly:

- **Blank screen on app launch:** `webDir` doesn't match your real build output. Run `npm run build`, confirm the folder exists, update `capacitor.config.*`, then run `npx cap sync`.
- **Web changes not visible in the native app:** you changed web code but skipped rebuild and sync. Always run `npm run build && npx cap sync` before testing in Xcode or Android Studio.
- **Plugin works on one platform only:** platform setup, permissions, or native project files are incomplete. Recheck the plugin installation docs, update the iOS and Android configs, then run `npx cap sync`.
- **iOS signing errors in Xcode:** team, bundle ID, provisioning profile, and certificate are misaligned. Verify those values in Xcode and your Apple Developer account.
- **Android build failures after SDK updates:** Gradle and Android SDK versions drift apart. Align toolchain versions and run a clean rebuild in Android Studio.

## Performance Tips in the WebView

Performance issues in mobile WebViews usually come from oversized assets and heavy startup work. These practices consistently improve startup time and runtime responsiveness:

- Prefer native plugins for device-heavy operations when possible. For example, the [Capacitor Audio Player plugin](../../sdks/capacitor/audio-player.md) uses native playback capabilities and supports background audio, which is important for uninterrupted playback when the app isn't in the foreground. In real projects, native integrations can also improve reliability and throughput for large file operations, as described in [How to Wrap an Angular App with Capacitor and Firebase](./how-to-wrap-an-angular-app-with-capacitor-and-firebase.md).
- Keep your initial JavaScript bundle small with route-based code splitting, and defer non-critical scripts until after first render.
- Optimize images and use modern formats where possible — they're cheap on a dev machine and expensive on a phone.
- Avoid large synchronous tasks during app startup, since they block the WebView from rendering the first paint.
- Test on lower-end Android devices (not just current flagships) and watch memory-heavy screens like file previews and long lists, which are where mobile WebViews crack first.

## Automating Build and Deploy with Capawesome Cloud

Once your app runs reliably on real devices, the next step is automating delivery. Capawesome Cloud handles the parts that would otherwise eat your time: [Native Builds](../../cloud/native-builds/setup.md) produces reproducible iOS and Android binaries in a consistent environment, [Live Updates](../../cloud/live-updates/setup.md) ships web-layer releases between store submissions, and [App Store Publishing](../../cloud/app-store-publishing/index.md) delivers builds straight to TestFlight or Google Play.

A pattern that works well in practice is combining the two release tracks — ship stable native builds through the stores, and use Live Updates for fast iteration on the web layer in between. Wire it all together with [Automations](../../cloud/automations/index.md) so the build and deploy pipeline triggers automatically on commit, branch, or tag.

## Try Capawesome Cloud

Automate native builds, Live Updates, and app store delivery so your team can focus on product work instead of release chores.

[Try Capawesome Cloud Free](https://capawesome.io){ .md-button .md-button--primary }

## FAQ

### Does Capacitor work with frameworks other than React, Angular, Vue, and Svelte?

Yes. Capacitor only cares about your final build output directory, not the framework that produced it, so any framework that compiles down to static HTML/CSS/JS — Solid, Qwik, plain Vite, or a static site generator — works the same way. See [Capacitor integrations](https://capawesome.io/integrations/capacitor/){:target="_blank"} for the broader picture of what Capawesome Cloud supports on top of it. You just need to point `webDir` in `capacitor.config.ts` at whatever folder your build command actually outputs.

### Why does my app show a blank white screen after I add Capacitor?

Almost always a `webDir` mismatch. If the path in `capacitor.config.ts` doesn't match your framework's real build output folder (`dist`, `build`, `www`, `dist/<project>/browser`, etc.), Capacitor packages an empty or wrong directory into the native shell. Run your build command, confirm the output folder's actual name, update `webDir` to match, then re-run `npx cap sync`.

### I changed my web code but the native app still shows the old version — why?

You skipped the build-and-sync step. Capacitor's native projects contain a static copy of your last build output, not a live reference to your source code, so every change needs `npm run build` followed by `npx cap sync` before it shows up in Xcode or Android Studio. Live reload (`npx cap run ios -l --external`) is the exception — it points the native shell at your dev server directly for faster iteration, but should be disabled before shipping a real build.

### Do I need a new App Store submission every time I fix a bug?

Depends on where the bug lives. If it's in your web layer (JS logic, CSS, markup), [Live Updates](../../cloud/live-updates/index.md) can ship the fix directly to installed apps without app store review. If the bug is in native configuration or a native plugin, you need a new native build and store submission — see the decision matrix above for the exact boundary.

### Will a plugin that works on iOS also work on Android and the web?

Not automatically — check each plugin's own compatibility table before relying on it. Plugin support varies by platform: some, like the [Capacitor Camera plugin](https://capacitorjs.com/docs/apis/camera){:target="_blank"}, cover iOS, Android, and web, while others are platform-specific or have partial web support. If a plugin "works on one platform only" during testing, it's usually incomplete platform setup or missing permissions rather than a bug — recheck the plugin's installation docs for each platform you target.

## Conclusion

Capacitor gives teams a practical way to ship iOS and Android apps from a single modern web codebase. The core workflow stays consistent across React, Angular, Vue, Svelte, and other frameworks: build web assets, sync the native shells, validate on devices, and automate delivery.

Ready to take your app all the way to the App Store? We've put together a complete end-to-end guide that covers every step — from Capacitor setup through certificates, cloud builds, store listing preparation, and setting up Live Updates post-launch: [From Web App to App Store: The Complete Guide](./11-steps-to-get-your-web-app-on-the-app-store.md).

Built your web app with an AI app builder instead? Our beginner-friendly walkthrough [Convert Your Lovable App to iOS & Android Apps](./convert-lovable-app-to-mobile-app.md) takes a Lovable app from the browser to both stores — no Mac required.

If you have questions, join the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"} and ask us anything related to Capacitor. To stay up to date on new guides and releases, subscribe to the [Capawesome newsletter](https://capawesome.io/newsletter/){:target="_blank"}.
