# Ionic Capacitor

> >

- **Type:** Skill
- **Install:** `agentstack add skill-gabriel-tutor-ionic-capacitor-skills-ionic-capacitor`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [gabriel-tutor](https://agentstack.voostack.com/s/gabriel-tutor)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [gabriel-tutor](https://github.com/gabriel-tutor)
- **Source:** https://github.com/gabriel-tutor/ionic-capacitor-skills/tree/main/ionic-capacitor

## Install

```sh
agentstack add skill-gabriel-tutor-ionic-capacitor-skills-ionic-capacitor
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Ionic Capacitor Development Skill

Build performant, native-feeling mobile apps using Ionic UI components and Capacitor's native runtime. This skill covers the full stack: project setup, component patterns, state management, native APIs, theming, and deployment across React, Angular, and Vue.

## Quick Decision: Which Framework?

| Factor | React | Angular | Vue |
|--------|-------|---------|-----|
| Learning curve | Moderate | Steeper | Gentle |
| Ecosystem | Largest | Enterprise-strong | Growing fast |
| State management | Zustand/TanStack Query | Signals + Services | Pinia |
| Best for | Startups, flexibility | Enterprise, structure | Simplicity, speed |
| Ionic integration | `@ionic/react` | `@ionic/angular` | `@ionic/vue` |

All three are first-class citizens in Ionic. Pick based on team familiarity. Read `references/react.md`, `references/angular.md`, or `references/vue.md` for framework-specific deep dives.

## Project Setup

### Create a New Project

```bash
# Install Ionic CLI
npm install -g @ionic/cli

# Create project (pick your framework)
ionic start myApp tabs --type=react      # React
ionic start myApp tabs --type=angular    # Angular
ionic start myApp tabs --type=vue        # Vue

# Templates: blank, tabs, sidemenu, list, conference
```

### Project Structure (Universal)

```
myApp/
├── src/
│   ├── components/       # Reusable UI components
│   ├── pages/            # Route-level page components
│   ├── services/         # API calls, business logic
│   ├── hooks/            # Custom hooks (React/Vue)
│   ├── store/            # State management
│   ├── theme/
│   │   └── variables.css # Ionic CSS custom properties
│   ├── assets/           # Static files (images, fonts)
│   └── App.tsx           # Root component
├── public/
├── capacitor.config.ts   # Capacitor configuration
├── ionic.config.json     # Ionic CLI configuration
└── package.json
```

### Capacitor Configuration

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

const config: CapacitorConfig = {
  appId: 'com.yourcompany.appname',    // Reverse domain notation
  appName: 'Your App Name',
  webDir: 'dist',                       // Build output directory
  server: {
    // For development live reload (remove in production):
    // url: 'http://192.168.1.x:8100',
    // cleartext: true,
    androidScheme: 'https',             // Always use https
  },
  ios: {
    contentInset: 'automatic',          // Safe area handling
    preferredContentMode: 'mobile',
  },
  android: {
    allowMixedContent: false,           // Security: keep false in prod
  },
  plugins: {
    SplashScreen: {
      launchAutoHide: true,
      androidScaleType: 'CENTER_CROP',
      splashFullScreen: true,
      splashImmersive: true,
    },
    StatusBar: {
      style: 'dark',                    // 'dark' or 'light'
    },
  },
};

export default config;
```

### Add Native Platforms

```bash
# Build web assets first
ionic build

# Add platforms
ionic cap add ios
ionic cap add android

# Sync web assets to native projects
ionic cap sync

# Open native IDEs
ionic cap open ios      # Opens Xcode
ionic cap open android  # Opens Android Studio
```

## Core Component Patterns

### Page Structure — The Foundation

Every page follows this pattern. Never skip it — Ionic's scroll behavior, safe areas, and transitions depend on this structure.

```tsx

  
    
      Page Title
    
  

  
    {/* iOS large title support */}
    
      
        Page Title
      
    

    {/* Your page content */}
  

```

The `collapse="condense"` header creates the native iOS large-title-to-small-title scroll effect. Always include it for iOS native feel.

### Navigation Patterns

**Tabs** — Best for apps with 3-5 top-level sections:
```tsx

  
    
    
    
  
  
    
      
      Home
    
    {/* ... */}
  

```

**Side Menu** — Best for apps with many sections:
```tsx

  
    
      
        Menu
      
    
    
      
        
          Page 1
        
      
    
  
  

```

**Stack Navigation** — For drill-down flows:
```tsx

```
Always set `defaultHref` so back navigation works even when the user deep-links into a page.

### Lists — The Workhorse Component

Lists appear everywhere in mobile apps. Use the right pattern:

```tsx
{/* Basic list */}

  
    
      Primary Text
      Secondary text
    
  

{/* Sliding items with actions */}

  
    
      Archive
    
    
      Swipe me
    
    
      Delete
    
  

{/* Virtual scroll for large lists (performance critical) */}
{/* Use framework-specific virtual scroll or @ionic/react's IonContent */}
```

### Grid Layout — Responsive Image/Card Galleries

Use `IonGrid` with `IonRow` and `IonCol` for responsive grid layouts like photo galleries, dashboards, or card grids. This is the correct approach for multi-column layouts (not IonList):

```tsx
{/* Photo/Image Gallery — use IonGrid, NOT IonList */}

  
    {photos.map((photo) => (
      
        
          
          
            {photo.date}
          
        
      
    ))}
  

{/* Dashboard stats grid */}

  
    
    
    
    
  

```

Column sizing: `size` sets the default (out of 12 columns). Use `sizeSm`, `sizeMd`, `sizeLg`, `sizeXl` for responsive breakpoints. For example, `size="6" sizeMd="4" sizeLg="3"` gives 2 columns on mobile, 3 on tablet, 4 on desktop.

### Forms — Getting Input Right

```tsx

  
    
  

  
    
  

  
    
      Bug Report
      Feature Request
    
  

  
    Enable notifications
  

  
    
      0
      100
    
  

```

Use `labelPlacement="stacked"` and `fill="outline"` for modern form styling. Use `fill="solid"` for a softer look.

### Overlays — Modals, Alerts, Action Sheets

```tsx
{/* Modal — Present complex UI */}
 setShowModal(false)}
  breakpoints={[0, 0.5, 1]}       // Sheet modal with stops
  initialBreakpoint={0.5}          // Start half-open
  handle={true}                     // Show drag handle
>
  
    {/* Modal content */}
  

{/* Alert — Simple decisions */}
const [presentAlert] = useIonAlert();
presentAlert({
  header: 'Confirm',
  message: 'Are you sure?',
  buttons: ['Cancel', { text: 'OK', role: 'confirm' }],
});

{/* Action Sheet — List of actions */}
const [presentActionSheet] = useIonActionSheet();
presentActionSheet({
  header: 'Actions',
  buttons: [
    { text: 'Share', icon: shareOutline },
    { text: 'Delete', role: 'destructive', icon: trashOutline },
    { text: 'Cancel', role: 'cancel' },
  ],
});

{/* Toast — Non-intrusive feedback */}
const [presentToast] = useIonToast();
presentToast({
  message: 'Item saved!',
  duration: 2000,
  position: 'bottom',
  color: 'success',
});

{/* Loading — Block UI during operations */}
const [presentLoading, dismissLoading] = useIonLoading();
await presentLoading({ message: 'Saving...' });
await saveData();
await dismissLoading();
```

### Cards — Content Containers

```tsx

  
  
    Category
    Card Title
  
  
    Card description text goes here.
  
  
    
      
        
      
    
  

```

### Pull-to-Refresh and Infinite Scroll

```tsx

  
    
  

  {/* Your list content */}

  
    
  

```

### Skeleton Loading — Perceived Performance

```tsx
{isLoading ? (
  
    {[...Array(5)].map((_, i) => (
      
        
          
        
        
          
          
        
      
    ))}
  
) : (
  
)}
```

## Theming — Native Look and Feel

### Platform-Adaptive Styling

Ionic automatically adapts to iOS and Material Design. The mode is set per-platform but can be overridden:

```typescript
// Force a specific mode globally
// In app setup:
setupIonicReact({ mode: 'ios' });  // or 'md'
```

### CSS Custom Properties

Ionic's design system is built on CSS variables. Override them in `theme/variables.css`:

```css
:root {
  /* Primary brand color — generates shade/tint automatically */
  --ion-color-primary: #3880ff;
  --ion-color-primary-rgb: 56, 128, 255;
  --ion-color-primary-contrast: #ffffff;
  --ion-color-primary-shade: #3171e0;
  --ion-color-primary-tint: #4c8dff;

  /* Application-level variables */
  --ion-background-color: #ffffff;
  --ion-text-color: #000000;
  --ion-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* Stepped colors (for subtle backgrounds) */
  --ion-color-step-50: #f2f2f2;
  --ion-color-step-100: #e6e6e6;
  /* ... up to step-950 */
}
```

Use the [Ionic Color Generator](https://ionicframework.com/docs/theming/color-generator) to produce correct shade/tint/contrast values.

### Dark Mode

Three approaches (pick one):

```css
/* 1. Always dark */
@import '@ionic/react/css/palettes/dark.always.css';

/* 2. Follow system preference (recommended) */
@import '@ionic/react/css/palettes/dark.system.css';

/* 3. Toggle via CSS class */
@import '@ionic/react/css/palettes/dark.class.css';
/* Then add/remove .ion-palette-dark class on  */
```

Add this meta tag for native UI elements:
```html

```

### Per-Component Styling

Use CSS Shadow Parts and CSS variables:
```css
/* Target shadow DOM parts */
ion-button::part(native) {
  border-radius: 20px;
}

/* Component-level CSS variables */
ion-item {
  --background: transparent;
  --border-color: var(--ion-color-light);
  --padding-start: 16px;
}
```

### CSS Utility Classes

Ionic provides utility classes for common styling. Import them in your global CSS:

```css
/* Import all utilities at once */
@import '@ionic/react/css/ionic.bundle.css';  /* React */
@import '@ionic/angular/css/ionic.bundle.css'; /* Angular */
@import '@ionic/vue/css/ionic.bundle.css';     /* Vue */
```

```html

Padded content
Top margin, no padding

Centered text

Mobile only
Tablet & desktop only
```

> For complete utility class reference, Shadow Parts, and customization recipes, read `references/styling.md`.

## Capacitor Native APIs

### Installation Pattern

```bash
npm install @capacitor/camera    # Install the plugin
ionic cap sync                   # Sync to native projects
```

### Camera

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

const takePhoto = async () => {
  const image = await Camera.getPhoto({
    quality: 90,
    allowEditing: false,
    resultType: CameraResultType.Uri,      // or Base64, DataUrl
    source: CameraSource.Camera,           // or Photos, Prompt
    width: 1024,                           // Max width
    saveToGallery: true,
  });
  return image.webPath;  // Use this for 
};
```

### Geolocation

```typescript
import { Geolocation } from '@capacitor/geolocation';

const getCurrentPosition = async () => {
  const coordinates = await Geolocation.getCurrentPosition({
    enableHighAccuracy: true,
    timeout: 10000,
  });
  return {
    lat: coordinates.coords.latitude,
    lng: coordinates.coords.longitude,
  };
};

// Watch position changes
const watchId = await Geolocation.watchPosition(
  { enableHighAccuracy: true },
  (position, err) => { /* handle updates */ }
);
// Later: Geolocation.clearWatch({ id: watchId });
```

### Push Notifications

```typescript
import { PushNotifications } from '@capacitor/push-notifications';

const initPush = async () => {
  const permStatus = await PushNotifications.requestPermissions();
  if (permStatus.receive === 'granted') {
    await PushNotifications.register();
  }

  PushNotifications.addListener('registration', (token) => {
    console.log('FCM token:', token.value);
    // Send token to your server
  });

  PushNotifications.addListener('pushNotificationReceived', (notification) => {
    console.log('Push received:', notification);
  });

  PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
    console.log('Push action:', action);
    // Navigate based on action.notification.data
  });
};
```

### Filesystem

```typescript
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';

// Write a file
await Filesystem.writeFile({
  path: 'data/config.json',
  data: JSON.stringify(config),
  directory: Directory.Data,
  encoding: Encoding.UTF8,
  recursive: true,  // Create directories as needed
});

// Read a file
const result = await Filesystem.readFile({
  path: 'data/config.json',
  directory: Directory.Data,
  encoding: Encoding.UTF8,
});
```

### Other Core Plugins

| Plugin | Install | Key Methods |
|--------|---------|-------------|
| `@capacitor/app` | `npm i @capacitor/app` | `App.addListener('appStateChange')`, `App.getLaunchUrl()` |
| `@capacitor/browser` | `npm i @capacitor/browser` | `Browser.open({ url })` |
| `@capacitor/clipboard` | `npm i @capacitor/clipboard` | `Clipboard.write()`, `Clipboard.read()` |
| `@capacitor/device` | `npm i @capacitor/device` | `Device.getInfo()`, `Device.getId()` |
| `@capacitor/haptics` | `npm i @capacitor/haptics` | `Haptics.impact()`, `Haptics.vibrate()` |
| `@capacitor/keyboard` | `npm i @capacitor/keyboard` | `Keyboard.show()`, `Keyboard.hide()` |
| `@capacitor/network` | `npm i @capacitor/network` | `Network.getStatus()`, `Network.addListener()` |
| `@capacitor/preferences` | `npm i @capacitor/preferences` | `Preferences.set()`, `Preferences.get()` |
| `@capacitor/share` | `npm i @capacitor/share` | `Share.share({ title, text, url })` |
| `@capacitor/splash-screen` | `npm i @capacitor/splash-screen` | `SplashScreen.hide()`, `SplashScreen.show()` |
| `@capacitor/status-bar` | `npm i @capacitor/status-bar` | `StatusBar.setStyle()`, `StatusBar.hide()` |
| `@capacitor/action-sheet` | `npm i @capacitor/action-sheet` | `ActionSheet.showActions({ options })` |
| `@capacitor/app-launcher` | `npm i @capacitor/app-launcher` | `AppLauncher.canOpenUrl()`, `openUrl()` |
| `@capacitor/dialog` | `npm i @capacitor/dialog` | `Dialog.alert()`, `confirm()`, `prompt()` |
| `@capacitor/google-maps` | `npm i @capacitor/google-maps` | `GoogleMap.create()`, `addMarker()`, `setCamera()` |
| `@capacitor/local-notifications` | `npm i @capacitor/local-notifications` | `LocalNotifications.schedule()`, `requestPermissions()` |
| `@capacitor/motion` | `npm i @capacitor/motion` | `Motion.addListener('accel')`, `'orientation'` |
| `@capacitor/screen-reader` | `npm i @capacitor/screen-reader` | `ScreenReader.isEnabled()`, `speak()` |
| `@capacitor/text-zoom` | `npm i @capacitor/text-zoom` | `TextZoom.get()`, `set()`, `getPreferred()` |
| `@capacitor/toast` | `npm i @capacitor/toast` | `Toast.show({ text, position, duration })` |

> For full plugin documentation with code examples, read `references/native-plugins.md`.

## Performance Optimization

### Lazy Loading Routes

Split your app into chunks. Load pages only when navigated to:

```tsx
// React
const Tab1 = React.lazy(() => import('./pages/Tab1'));
const Tab2 = React.lazy(() => import('./pages/Tab2'));

// Wrap in Suspense with skeleton fallback
}>
  

```

### Image & Render Optimization

- Use `` instead of `` for lazy loading; `` in lists
- Memoize with `useMemo`/`useCallback` (React), `computed` (Vue), `trackBy` (Angular)
- Avoid inline object creation in JSX props

### Bundle Size

Import only what you use: `import { IonButton } from '@ionic/react'` — never `import * as Ionic`. Analyze with `npx source-map-explorer dist/assets/*.js`.

### Virtual Scrolling for Large Lists

For lists with 100+ items, use virtual scrolling to render only visible items. Each framework has its approach — see the framework-specific reference files.

## Development Workflow

```bash
ionic serve                           # Browser preview
ionic cap run ios -l --external       # Live reload on iOS device
ionic cap run android -l --external   # Live reload on Android
ionic build && ionic cap sync         # Build + sync to native projects
ionic cap open ios                    # Open Xcode
ionic cap open android                # Open Android Studio
```

**Debugging:** Browser via `ionic serve` DevTools • iOS via Safari > Develop > [Device] • Android via `chrome://inspect`

## Deployment

**iOS:** Apple Developer account ($99/yr) → configure signing in Xcode → set Bundle ID matching `appId` → Product > Archive → uploa

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [gabriel-tutor](https://github.com/gabriel-tutor)
- **Source:** [gabriel-tutor/ionic-capacitor-skills](https://github.com/gabriel-tutor/ionic-capacitor-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-gabriel-tutor-ionic-capacitor-skills-ionic-capacitor
- Seller: https://agentstack.voostack.com/s/gabriel-tutor
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
