# Capacitor

> This skill should be used when the user asks to "build a Capacitor app", "add Capacitor to a web project", "use Capacitor plugins", "configure Capacitor for iOS or Android", or needs guidance on Capacitor best practices, security, storage, deep links, or the development workflow.

- Skill: `the-perfect-developer/capacitor` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add the-perfect-developer/capacitor`
- Raw SKILL.md: https://api.skillmd.com/api/skills/the-perfect-developer/capacitor/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: the-perfect-developer (https://skillmd.com/u/the-perfect-developer)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/the-perfect-developer/capacitor

---


# Capacitor: Cross-Platform Native Runtime

Capacitor is a cross-platform native runtime for building Web Native apps that run on iOS, Android, and the web from a single modern web codebase. It provides a consistent, web-focused API layer that bridges JavaScript to native device features via a plugin system.

## Core Workflow

The canonical development loop is:

1. **Build web assets**: `npm run build` (or `ng build`, `vite build`, etc.)
2. **Sync to native projects**: `npx cap sync`
   - Copies the web bundle into iOS and Android native projects
   - Installs/updates native plugin dependencies
   - Run this after every web build or plugin change
3. **Test on device**:
   ```bash
   npx cap run ios
   npx cap run android
   ```
4. **Open native IDE** when native changes are needed:
   ```bash
   npx cap open ios      # opens Xcode
   npx cap open android  # opens Android Studio
   ```
5. **Build a release binary**:
   ```bash
   npx cap build android  # outputs signed AAB/APK
   npx cap build ios      # outputs IPA
   ```

Keep `@capacitor/core`, `@capacitor/ios`, and `@capacitor/android` on identical versions at all times. Update all together:

```bash
npm i @capacitor/core @capacitor/ios @capacitor/android
npm i -D @capacitor/cli
```

## Configuration

Capacitor-level settings live in `capacitor.config.ts` (or `.json`). This file controls tooling only — not native runtime behavior. Key fields:

| Field | Purpose |
|---|---|
| `appId` | Bundle identifier (e.g. `com.example.app`) |
| `appName` | Display name |
| `webDir` | Output directory of the web build (e.g. `dist`, `build`, `www`) |
| `server.url` | Override for live reload (remove before committing) |

Platform-specific runtime configuration is done in native IDEs: `ios/App/App/Info.plist` and `AndroidManifest.xml` for Android. Do not attempt to manage those via the Capacitor config file.

## Plugins

Capacitor plugins expose native APIs to JavaScript. Three sources exist:

- **Official plugins** (`@capacitor/*`) — maintained by the Capacitor team, covering Camera, Filesystem, Geolocation, Push Notifications, Preferences, etc.
- **Community plugins** (`@capacitor-community/*`) — curated third-party ecosystem
- **Cordova plugins** — compatibility layer; prefer Capacitor-native equivalents

Install a plugin, then sync:

```bash
npm install @capacitor/camera
npx cap sync
```

Always check whether a plugin requires additional native configuration (entitlements on iOS, permissions in `AndroidManifest.xml`). The plugin's README will specify.

### Mocking Plugins in Tests

Capacitor plugins are JavaScript proxies; wrapping a proxy in another proxy fails. Use **manual mocks** instead.

**Jest** — place stubs under `__mocks__/@capacitor/`:

```ts
// __mocks__/@capacitor/preferences.ts
export const Preferences = {
  async get(data: { key: string }) { return { value: undefined }; },
  async set(_data: { key: string; value: string }) {},
  async remove(_data: { key: string }) {},
  async clear() {},
};
```

Jest auto-discovers files from `__mocks__/` before `node_modules`.

**Jasmine/Angular** — add a `paths` mapping in `tsconfig.spec.json`:

```json
"paths": {
  "@capacitor/*": ["__mocks__/@capacitor/*"]
}
```

Note: `paths` in `tsconfig.spec.json` replaces (does not merge with) the base `tsconfig.json` paths — include all existing entries.

## Storage

Do not rely on `localStorage` or `IndexedDB` as primary storage on mobile. The OS reclaims both when storage is low. IndexedDB on iOS is not persisted by default.

| Use case | Recommended solution |
|---|---|
| Small key/value data (settings, tokens) | `@capacitor/preferences` |
| Large datasets / SQL queries | SQLite plugin (`capacitor-sqlite`) |
| Sensitive values (encryption keys, auth tokens) | iOS Keychain / Android Keystore via a plugin |

```ts
import { Preferences } from '@capacitor/preferences';

// Write
await Preferences.set({ key: 'theme', value: 'dark' });

// Read
const { value } = await Preferences.get({ key: 'theme' });

// Remove
await Preferences.remove({ key: 'theme' });
```

`Preferences` values are stored natively and survive app updates and low-storage eviction.

## Security

### Secrets and API Keys

Never embed secrets in web app code. The JavaScript bundle ships inside the app binary and is trivially extractable. Move secret-dependent logic server-side (serverless function or backend API). If a token must exist on device (e.g. auth token), store it only in memory or in the native Keychain/Keystore — never in `localStorage` or `Preferences` unencrypted.

### Network

Only make requests to `https://` endpoints. Never use `http://` in production builds.

### Content Security Policy

Add a CSP `<meta>` tag in `index.html` to restrict resource loading:

```html
<meta
  http-equiv="Content-Security-Policy"
  content="default-src 'self'; connect-src 'self' https://api.example.com"
/>
```

### oAuth2 / Deep Link Authentication

Custom URL schemes (e.g. `myapp://`) are not domain-controlled and can be intercepted by a malicious app. Never pass sensitive tokens through a custom scheme. Use **Universal Links** (iOS) or **App Links** (Android) for auth callbacks, and always enable **PKCE** in oAuth2 flows.

## Deep Links (Universal Links / App Links)

Deep links allow HTTPS URLs to open specific screens inside the native app. Prefer Universal/App Links over custom URL schemes — they require verified web domain ownership.

### Listen for Incoming Links

Register the listener early (e.g. app bootstrap):

```ts
import { App } from '@capacitor/app';

App.addListener('appUrlOpen', (event) => {
  const path = new URL(event.url).pathname;
  // hand off to your router
  router.navigate(path);
});
```

### Required Setup (both platforms)

1. Host a site association file at `https://yourdomain.com/.well-known/`:
   - iOS: `apple-app-site-association` (no extension, JSON)
   - Android: `assetlinks.json`
2. Configure the native app:
   - iOS: enable Associated Domains in Xcode (`applinks:yourdomain.com`)
   - Android: add `intent-filter` with `android:autoVerify="true"` in `AndroidManifest.xml`

See `references/security-and-deeplinks.md` for complete file formats and intent filter XML.

## Live Reload (Development Only)

Live reload reloads the WebView when web source files change — no native rebuild required.

**With Ionic CLI (recommended):**

```bash
npm install -g @ionic/cli native-run
ionic cap run ios -l --external
ionic cap run android -l --external
```

**Without Ionic CLI** — add a `server` entry to `capacitor.config.json`:

```json
"server": {
  "url": "http://192.168.1.68:8100",
  "cleartext": true
}
```

Run `npx cap copy` to push the config update, then launch from the native IDE.

**Never commit the `server` config to source control.** Remove it before building for release.

## Quick Reference

| Task | Command |
|---|---|
| Sync web build to native | `npx cap sync` |
| Copy web assets only | `npx cap copy` |
| Open iOS project | `npx cap open ios` |
| Open Android project | `npx cap open android` |
| Run on device | `npx cap run ios` / `npx cap run android` |
| Build release binary | `npx cap build ios` / `npx cap build android` |
| Update Capacitor | `npm i @capacitor/core @capacitor/ios @capacitor/android` |

## Additional Resources

- **`references/plugins-and-storage.md`** — Plugin installation patterns, Preferences API reference, SQLite options, and testing stubs in detail
- **`references/security-and-deeplinks.md`** — CSP patterns, Keychain/Keystore guidance, full Universal Links / App Links file formats, PKCE requirements

