Skip to content
Back to skills

Capacitor

ASecurity

Capacitor is a runtime that packages a web app as a native iOS and Android app and exposes native device APIs to JavaScript. Use when someone asks to "convert my website to a mobile app", "Capacitor", "web to native app", "access camera from JavaScript", "deploy web app to App Store", "hybrid mobile app", or "Ionic Capacitor". Covers native API access, plugin system, web-to-native bridge, and app store deployment.

  • 142 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
developmentjavascripttypescriptgojavaswiftkotlinbashreactvueangular

Works with

  • terminal
  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill capacitor --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Capacitor?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Capacitor
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-capacitor/badge)](https://www.skillsdirectory.com/skills/terminalskills-capacitor)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: capacitor
description: >-
  Capacitor is a runtime that packages a web app as a native iOS and Android
  app and exposes native device APIs to JavaScript. Use when someone asks to
  "convert my website to a mobile app", "Capacitor", "web to native app",
  "access camera from JavaScript", "deploy web app to App Store", "hybrid
  mobile app", or "Ionic Capacitor".
  Covers native API access, plugin system, web-to-native bridge, and app
  store deployment.
license: Apache-2.0
compatibility: "Capacitor 8: Node.js 22+, any web framework (React, Vue, Angular, Svelte). iOS builds need macOS with Xcode 26+; Android builds need Android Studio 2025.2.1+."
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: development
  repository: https://github.com/ionic-team/capacitor
  tags: ["mobile", "capacitor", "hybrid", "native", "ios"]
---

# Capacitor

## Overview

Capacitor wraps your web app in a native container and gives it access to native device APIs — camera, file system, push notifications, biometrics, geolocation, and more. Your existing React/Vue/Angular/Svelte app becomes an iOS and Android app without rewriting in Swift or Kotlin. Built by the Ionic team, it's the modern replacement for Cordova/PhoneGap. This skill targets Capacitor 8 (iOS 15+, Android 7 / API 24+).

## When to Use

- Have a web app and want native iOS/Android versions
- Need native device features (camera, push notifications, biometrics)
- Want one codebase for web + iOS + Android
- Converting a PWA to a native app for App Store distribution
- Team knows web tech but not Swift/Kotlin

## Instructions

### Setup

The web project needs a `package.json` and a build output directory (`dist`, `build`, `www`) whose `index.html` has a `<head>` tag.

```bash
npm install @capacitor/core
npm install -D @capacitor/cli
npx cap init "Trailhead" dev.fernhill.trailhead --web-dir dist

# Add platforms
npm install @capacitor/ios @capacitor/android
npx cap add ios        # Swift Package Manager project (default since Capacitor 8)
npx cap add android
```

`cap init` writes `capacitor.config.ts` (or `.json` when TypeScript is not installed) with the first three keys; plugin settings are added by hand. With TypeScript 7 the CLI loads the `.ts` config through Node's ES-module loader, so `package.json` needs `"type": "module"` (Vite templates have it); without it every `cap` command stops with `Parsing capacitor.config.ts failed`:

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

const config: CapacitorConfig = {
  appId: "dev.fernhill.trailhead",
  appName: "Trailhead",
  webDir: "dist",
  plugins: {
    PushNotifications: { presentationOptions: ["badge", "sound", "alert"] },
  },
};

export default config;
```

### Build and Run

```bash
# Build your web app first
npm run build

# Copy web assets to native projects and update native dependencies
npx cap sync

# Open in native IDE
npx cap open ios      # Opens Xcode
npx cap open android  # Opens Android Studio

# Or run directly (run = sync + build + deploy)
npx cap run ios --list                      # available simulators and devices
npx cap run ios --target-name "iPhone 17"   # or --target with an id from --list
npx cap run android

# Live reload from a running dev server (e.g. Vite on port 5173)
npx cap run android --live-reload --port 5173

# Signed release build from the terminal
npx cap build android --androidreleasetype AAB \
  --keystorepath release.keystore --keystorepass "$ANDROID_KEYSTORE_PASSWORD" \
  --keystorealias upload --keystorealiaspass "$ANDROID_KEY_PASSWORD"
```

### Native Plugins

```bash
# Install plugins
npm install @capacitor/camera @capacitor/filesystem @capacitor/push-notifications
npm install @capacitor/haptics @capacitor/share @capacitor/browser
npx cap sync
```

```typescript
// src/native/camera.ts — Access the device camera (@capacitor/camera 8.1+)
import { Camera, CameraDirection, MediaTypeSelection } from "@capacitor/camera";

export async function takePhoto(): Promise<string | undefined> {
  const photo = await Camera.takePhoto({
    quality: 80,
    cameraDirection: CameraDirection.Rear,
    saveToGallery: false,
  });
  return photo.webPath;  // Path to display in an <img> element
}

export async function pickFromGallery(): Promise<string[]> {
  const { results } = await Camera.chooseFromGallery({
    mediaType: MediaTypeSelection.Photo,
    allowMultipleSelection: true,
    limit: 5,
  });
  return results.flatMap((item) => (item.webPath ? [item.webPath] : []));
}
```

`Camera.getPhoto()` and `Camera.pickImages()` still work but are deprecated since plugin version 8.1; new code should use `takePhoto` and `chooseFromGallery`.

```typescript
// src/native/notifications.ts — Push notifications (iOS and Android only)
import { PushNotifications } from "@capacitor/push-notifications";

export async function registerPush(sendTokenToServer: (token: string) => Promise<void>) {
  // Attach listeners before register() so the first event is not missed
  await PushNotifications.addListener("registration", (token) => {
    void sendTokenToServer(token.value);  // FCM token on Android, APNs token on iOS
  });
  await PushNotifications.addListener("registrationError", (err) => {
    console.error("Push registration failed:", err.error);
  });
  await PushNotifications.addListener("pushNotificationReceived", (notification) => {
    console.log("Received:", notification.title, notification.body);
  });

  let status = await PushNotifications.checkPermissions();
  if (status.receive === "prompt") status = await PushNotifications.requestPermissions();
  if (status.receive !== "granted") throw new Error("Push permission denied");
  await PushNotifications.register();
}
```

```typescript
// src/native/filesystem.ts — File system access
import { Directory, Encoding, Filesystem } from "@capacitor/filesystem";

export async function saveFile(data: string, filename: string) {
  await Filesystem.writeFile({
    path: filename,
    data: data,
    directory: Directory.Documents,
    encoding: Encoding.UTF8,  // Without an encoding, data must be base64
  });
}

export async function readFile(filename: string): Promise<string> {
  const result = await Filesystem.readFile({
    path: filename,
    directory: Directory.Documents,
    encoding: Encoding.UTF8,
  });
  return result.data as string;
}
```

### Platform Detection

```typescript
// src/utils/platform.ts — Adapt behavior per platform
import { Capacitor } from "@capacitor/core";
import { Share } from "@capacitor/share";

export const isNative = Capacitor.isNativePlatform();
export const platform = Capacitor.getPlatform(); // 'ios' | 'android' | 'web'
export const hasPush = Capacitor.isPluginAvailable("PushNotifications");

// Use the native share sheet when available, fall back to the clipboard
export async function shareLink(title: string, url: string) {
  const { value: canShare } = await Share.canShare();
  if (canShare) await Share.share({ title, url });
  else await navigator.clipboard.writeText(url);
}
```

## Examples

### Example 1: Convert a React app to mobile

**User prompt:** "I have a React web app built with Vite. Make it available on iOS and Android."

```bash
npm install @capacitor/core @capacitor/ios @capacitor/android
npm install -D @capacitor/cli
npx cap init "Trailhead" dev.fernhill.trailhead --web-dir dist
npm run build
npx cap add android && npx cap add ios
npx cap sync
npx cap run android
```

`cap add` creates the `android/` and `ios/` folders (commit them), and `cap sync` reports each step (Android part shown; iOS prints the same):

```text
✔ Copying web assets from dist to android/app/src/main/assets/public in 1.36ms
✔ Creating capacitor.config.json in android/app/src/main/assets in 229.50μs
✔ copy android in 3.65ms
✔ Updating Android plugins in 232.46μs
✔ update android in 8.14ms
[info] Sync finished in 0.021s
```

The app then installs and opens on the connected emulator or device. `npx cap doctor` lists installed versus latest Capacitor packages when something looks off.

### Example 2: Add camera and file upload

**User prompt:** "Add photo capture and file upload to my mobile web app."

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

Add `NSCameraUsageDescription`, `NSPhotoLibraryUsageDescription` and `NSPhotoLibraryAddUsageDescription` strings to `ios/App/App/Info.plist`, then upload the captured file:

```typescript
import { takePhoto } from "./native/camera";

export async function uploadReceipt(): Promise<void> {
  const webPath = await takePhoto();
  if (!webPath) return;
  const blob = await (await fetch(webPath)).blob();
  const form = new FormData();
  form.append("receipt", blob, "receipt.jpg");
  await fetch(`${import.meta.env.VITE_API_URL}/receipts`, { method: "POST", body: form });
}
```

On a device the native camera opens and the photo is posted as multipart form data; in a browser the same code falls back to a file input (or the PWA Elements camera UI if installed).

## Guidelines

- **`npx cap sync` after every web build and every plugin install** — copies assets and updates native dependencies
- **`npx cap run` for testing** — faster than opening IDE each time
- **Platform detection** — `Capacitor.isNativePlatform()` for conditional native features
- **Not every plugin runs on the web** — Camera, Filesystem and Share have web implementations; Push Notifications does not. Guard with `Capacitor.isPluginAvailable()`
- **Live reload for development** — `npx cap run ios --live-reload --host 192.168.1.68 --port 5173`; the dev server must listen on `0.0.0.0` and the device must be on the same network. Never commit a `server.url` entry in the config
- **Custom native code** — write Swift/Kotlin plugins when needed
- **App Store deployment** — archive in Xcode for iOS; Android Studio or `npx cap build android` for Android. iOS builds require a Mac
- **`capacitor.config.ts`** — configure server URL, plugins, app info
- **Permissions in native config** — camera, location, etc. need `Info.plist` usage descriptions; on Android the Camera plugin needs no permission unless `saveToGallery` is used
- **Push setup is native** — Android needs the Firebase `google-services.json` in `android/app/`; iOS needs the Push Notifications capability and the two `AppDelegate.swift` forwarding methods from the plugin README
- **Upgrading** — keep `@capacitor/core`, `cli`, `ios` and `android` on the same version; `npx cap migrate` moves a Capacitor 7 project to 8 (Node 22+, Xcode 26+, iOS 15 deployment target, `minSdkVersion` 24). New iOS projects use Swift Package Manager; pass `--packagemanager CocoaPods` to `cap add ios` to keep CocoaPods
- **Not for gaming** — great for content apps, tools, dashboards; use Unity/Flutter for games

Files in this skill

  • SKILL.md5.6 KB
  • _scores.json1.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…