Install
$ agentstack add skill-gabriel-tutor-ionic-capacitor-skills-ionic-capacitor ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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
# 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
// 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
# 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.
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:
Home
{/* ... */}
Side Menu — Best for apps with many sections:
Menu
Page 1
Stack Navigation — For drill-down flows:
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:
{/* 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):
{/* 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
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
{/* 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
Category
Card Title
Card description text goes here.
Pull-to-Refresh and Infinite Scroll
{/* Your list content */}
Skeleton Loading — Perceived Performance
{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:
// 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:
: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 to produce correct shade/tint/contrast values.
Dark Mode
Three approaches (pick one):
/* 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:
Per-Component Styling
Use CSS Shadow Parts and CSS variables:
/* 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:
/* 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 */
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
npm install @capacitor/camera # Install the plugin
ionic cap sync # Sync to native projects
Camera
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
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
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
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:
// 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 offor 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
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
- Source: gabriel-tutor/ionic-capacitor-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.