ionic-vue — Claude Code skill: Ionic apps built with Vue
Walks the agent through Ionic Vue development: project layout, routing, lifecycle hooks and composables. Useful when a page stops refreshing its data on revisit.
npx skills add capawesome-team/skills --skill ionic-vue -a claude-codeWhat ionic-vue does
The skill tells the agent how an Ionic Vue project is put together: where src/router/index.ts, App.vue and main.ts live, how to wire up IonicVue and @ionic/vue-router. It covers IonPage, IonRouterOutlet and IonTabs, navigation through useIonRouter, lazy-loaded routes and platform checks with isPlatform.
The part that earns its keep is lifecycle hooks: onIonViewWillEnter, onIonViewDidEnter, onIonViewWillLeave, onIonViewDidLeave. IonRouterOutlet keeps pages in the DOM, so Vue's onMounted fires once for the lifetime of a page rather than on every visit. Load your data in onMounted and the list will be stale when the user comes back to the tab. Move that call into onIonViewWillEnter.
You need Ionic Framework 7 or 8 with @ionic/vue, Vue 3, Node.js and npm. iOS builds want Xcode on macOS, Android builds want Android Studio. The agent reads package.json first to detect versions, checks for tsconfig.json and the android/ and ios/ folders, then writes code matching the project's existing style — <script setup> or Options API. Component theming, CLI usage, creating a new app, Capacitor without Ionic and Ionic version upgrades are handled by separate skills.
What it helps with
- find out why a page keeps showing stale data on revisit
- set up nested routes with
IonTabsand a separate stack per tab - swap
vue-routerforcreateRouterfrom@ionic/vue-router - call a web component method through
$el, like scrolling content to the bottom - detect the platform and branch behaviour for iOS or Android
Example prompts for the agent
- Walk through my Ionic Vue project structure and tell me what's wrong with the routing
- Why doesn't onIonViewWillEnter fire on my list page?
- Build tab navigation with IonTabs, each tab keeping its own stack
How to install ionic-vue
1Skills CLI (Vercel)
npx skills add capawesome-team/skills --skill ionic-vue -a claude-code2Manually: put the files in place
~/.claude/skills/ionic-vue/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/ionic-vue/
1Skills CLI (Vercel)
npx skills add capawesome-team/skills --skill ionic-vue -a codex2Manually: put the files in place
.agents/skills/ionic-vue/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add capawesome-team/skills --skill ionic-vue -a cursor2Manually: put the files in place
.cursor/skills/ionic-vue/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install capawesome-team/skills ionic-vue2Manually: put the files in place
.github/skills/ionic-vue/SKILL.md1Skills CLI (Vercel)
npx skills add capawesome-team/skills --skill ionic-vue -a gemini-cli2Platform CLI
gemini skills install https://github.com/capawesome-team/skills --path skills/ionic-vueManually: put the files in place
.devin/skills/ionic-vue/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add capawesome-team/skills --skill ionic-vue -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about ionic-vue
How do I install ionic-vue in Claude?
Run npx skills add capawesome-team/skills --skill ionic-vue -a claude-code. The skill lands at ~/.claude/skills/ionic-vue/SKILL.md.
What does the ionic-vue skill do?
It guides the agent through Ionic Vue patterns: project structure, components, navigation, lifecycle hooks and composables such as useIonRouter, useBackButton and useKeyboard. It also helps debug issues specific to the Vue flavour.
What do I need before using it?
Ionic Framework 7 or 8 with @ionic/vue, Vue 3, Node.js and npm. Xcode on macOS for iOS, Android Studio for Android.
Source text
SKILL.md · MIT
---
name: ionic-vue
description: "Guides the agent through Ionic Vue development patterns — project structure, Vue-specific Ionic components (IonPage, IonRouterOutlet, IonTabs), navigation with Vue Router and useIonRouter, Ionic lifecycle hooks (onIonViewWillEnter, onIonViewDidEnter, onIonViewWillLeave, onIonViewDidLeave), composable utilities (useIonRouter, useBackButton, useKeyboard), tab-based routing, lazy loading, platform detection with isPlatform, and troubleshooting common Vue-specific issues. Do not use for general Ionic component theming or CLI usage (use ionic-app-development), creating a new Ionic app (use ionic-app-creation), Capacitor-specific Vue patterns without Ionic (use capacitor-vue), upgrading Ionic versions (use ionic-app-upgrades), or non-Vue frameworks like Angular or React."
license: MIT
compatibility: "Requires Node.js and npm. Xcode on macOS is required for iOS and Android Studio for Android."
metadata:
author: capawesome-team
source: https://github.com/capawesome-team/skills/tree/main/skills/ionic-vue
---
# Ionic Vue
Develop Ionic apps with Vue — project structure, components, navigation, lifecycle hooks, composables, and Vue-specific patterns.
## Prerequisites
1. **Ionic Framework 7 or 8** with `@ionic/vue`.
2. **Vue 3**.
3. Node.js and npm installed.
4. For **iOS**: Xcode installed.
5. For **Android**: Android Studio installed.
## Agent Behavior
- **Auto-detect before asking.** Check the project for `package.json` dependencies (`@ionic/vue`, `vue`, `@capacitor/core`), platforms (`android/`, `ios/`), build tools (`vite.config.ts`), and TypeScript usage. Only ask the user when something cannot be detected.
- **Guide step-by-step.** Walk the user through the process one step at a time. Never present multiple unrelated questions at once.
- **Adapt to the project.** Detect whether the project uses `<script setup>` (Composition API) or Options API and generate code that matches the existing style. Default to `<script setup lang="ts">` for new files unless the project uses a different pattern.
## Procedures
### Step 1: Analyze the Project
Auto-detect the following by reading project files:
1. **Ionic version**: Read `@ionic/vue` version from `package.json`.
2. **Vue version**: Read `vue` version from `package.json`.
3. **Capacitor version**: Read `@capacitor/core` version from `package.json` (if present).
4. **TypeScript**: Check if `tsconfig.json` exists and if `.vue` files use `<script setup lang="ts">`.
5. **Platforms**: Check which directories exist (`android/`, `ios/`).
6. **Router config**: Locate the router file (typically `src/router/index.ts`).
7. **Project template**: Determine if the project uses tabs, side-menu, or blank template by examining the router configuration and `App.vue`.
### Step 2: Project Structure
A standard Ionic Vue project follows this structure:
```
project-root/
├── android/ # Android native project (generated by Capacitor)
├── ios/ # iOS native project (generated by Capacitor)
├── public/
├── src/
│ ├── components/ # Reusable Vue components
│ ├── composables/ # Custom composables
│ ├── router/
│ │ └── index.ts # Vue Router configuration (uses @ionic/vue-router)
│ ├── theme/
│ │ └── variables.css # Ionic CSS custom properties
│ ├── views/ # Page components (routed views)
│ ├── App.vue # Root component (contains IonApp + IonRouterOutlet)
│ └── main.ts # Entry point (installs IonicVue plugin)
├── capacitor.config.ts # Capacitor configuration
├── package.json
├── tsconfig.json
└── vite.config.ts
```
If the project does not follow this structure, adapt all guidance to the project's actual directory layout. Do **not** restructure the project unless the user explicitly asks.
### Step 3: App Entry Point and Root Component
The entry point `src/main.ts` installs the IonicVue plugin and mounts the app:
```typescript
import { createApp } from 'vue';
import { IonicVue } from '@ionic/vue';
import App from './App.vue';
import router from './router';
/* Ionic CSS */
import '@ionic/vue/css/core.css';
import '@ionic/vue/css/normalize.css';
import '@ionic/vue/css/structure.css';
import '@ionic/vue/css/typography.css';
import '@ionic/vue/css/padding.css';
import '@ionic/vue/css/float-elements.css';
import '@ionic/vue/css/text-alignment.css';
import '@ionic/vue/css/text-transformation.css';
import '@ionic/vue/css/flex-utils.css';
import '@ionic/vue/css/display.css';
/* Theme */
import './theme/variables.css';
const app = createApp(App).use(IonicVue).use(router);
router.isReady().then(() => {
app.mount('#app');
});
```
The root `App.vue` contains `IonApp` and `IonRouterOutlet`:
```vue
<template>
<ion-app>
<ion-router-outlet />
</ion-app>
</template>
<script setup lang="ts">
import { IonApp, IonRouterOutlet } from '@ionic/vue';
</script>
```
### Step 4: Components and IonPage
Read `references/components.md` for detailed component patterns.
Key rules:
1. **Import all Ionic components** from `@ionic/vue` — e.g., `import { IonButton, IonContent, IonPage } from '@ionic/vue'`.
2. **Every routed page must use `IonPage`** as its root template element. Without it, page transitions break and Ionic lifecycle hooks do not fire.
3. **Access Web Component methods via `$el`** — e.g., `contentRef.value.$el.scrollToBottom(300)`.
4. **Import icons as SVG references** from `ionicons/icons` — never pass icon names as strings.
5. **Use `v-model`** on Ionic form components (`IonInput`, `IonToggle`, `IonSelect`, `IonCheckbox`, `IonRange`).
6. **Use kebab-case** for Ionic event names in templates — e.g., `@ion-change`, `@ion-infinite`.
### Step 5: Navigation and Routing
Read `references/navigation.md` for detailed routing patterns.
Key principles:
1. **Import `createRouter` from `@ionic/vue-router`**, not from `vue-router`. The Ionic version wraps Vue Router to enable page transitions.
2. **Declarative navigation**: Use `router-link` attribute on Ionic components with optional `router-direction` and `router-animation`.
3. **Programmatic navigation**: Use the `useIonRouter` composable for Ionic-specific transitions, or `useRouter` from `vue-router` for standard navigation.
4. **Lazy load routes** with `component: () => import('@/views/DetailPage.vue')`.
5. **Tab routing**: Use nested routes with `IonTabs` and `IonRouterOutlet`. Each tab maintains its own navigation stack.
6. **Never cross-route between tabs** — only tab bar buttons should switch tabs. Use `IonModal` for views shared across tabs.
### Step 6: Lifecycle Hooks
Read `references/lifecycle.md` for detailed lifecycle patterns.
Key principles:
1. `IonRouterOutlet` keeps pages in the DOM. Vue's `onMounted` fires only once per page, not on every visit.
2. Use Ionic lifecycle hooks to run logic on every page visit:
- `onIonViewWillEnter` — Refresh data every time the page appears.
- `onIonViewDidEnter` — Start animations or focus inputs after the page transition finishes.
- `onIonViewWillLeave` — Save state or unsubscribe.
- `onIonViewDidLeave` — Clean up off-screen resources.
3. Ionic lifecycle hooks require `IonPage` as the root element.
4. Only router-mapped components receive lifecycle hooks — child components do not.
### Step 7: Composables and Utilities
Read `references/composables.md` for detailed composable documentation.
Available composables and utilities from `@ionic/vue`:
| Function | Purpose |
|----------|---------|
| `useIonRouter()` | Programmatic navigation with transition control |
| `useBackButton(priority, handler)` | Handle Android hardware back button |
| `useKeyboard()` | Reactive keyboard visibility and height |
| `onIonViewWillEnter(cb)` | Lifecycle: page about to show |
| `onIonViewDidEnter(cb)` | Lifecycle: page fully visible |
| `onIonViewWillLeave(cb)` | Lifecycle: page about to hide |
| `onIonViewDidLeave(cb)` | Lifecycle: page fully hidden |
| `isPlatform(name)` | Check current platform (`ios`, `android`, `hybrid`, etc.) |
| `getPlatforms()` | Get array of all matching platform identifiers |
### Step 8: Platform Detection
Use `isPlatform` from `@ionic/vue` to conditionally execute platform-specific logic:
```vue
<script setup lang="ts">
import { isPlatform } from '@ionic/vue';
const isIOS = isPlatform('ios');
const isAndroid = isPlatform('android');
const isNative = isPlatform('hybrid');
const isMobileWeb = isPlatform('mobileweb');
</script>
<template>
<div>
<p v-if="isNative">Running as a native app</p>
<p v-else>Running in a browser</p>
</div>
</template>
```
Supported identifiers: `android`, `capacitor`, `cordova`, `desktop`, `electron`, `hybrid`, `ios`, `ipad`, `iphone`, `mobile`, `mobileweb`, `phablet`, `pwa`, `tablet`.
### Step 9: Build and Run
After implementing changes:
```bash
npm run build
npx cap sync
npx cap run android
npx cap run ios
```
For development with live reload:
```bash
ionic serve
```
For native live reload:
```bash
ionic cap run android --livereload --external
ionic cap run ios --livereload --external
```
## Error Handling
- **`Failed to resolve component: ion-*`**: The Ionic component is not imported. Add the missing import from `@ionic/vue` in the `<script>` section (e.g., `import { IonButton } from '@ionic/vue'`).
- **Page transitions not working**: Verify that the page component uses `IonPage` as its root template element. Without `IonPage`, transitions and lifecycle hooks silently fail.
- **Ionic lifecycle hooks not firing**: Ensure `IonPage` is the root element and the component is directly mapped to a route in the router configuration. Lifecycle hooks do not fire on child components.
- **`Method on component is not a function`**: Access the underlying Web Component via `$el` — e.g., `ref.value.$el.scrollToBottom()`, not `ref.value.scrollToBottom()`.
- **Slot attribute deprecation warning**: Ionic uses Web Component slots, which linters misidentify as deprecated Vue 2 slots. Disable the ESLint rule: `'vue/no-deprecated-slot-attribute': 'off'`.
- **Event listeners not firing**: Use kebab-case event names in templates (`ion-modal-did-present`) and in `addEventListener` calls. Do not use camelCase.
- **`useIonRouter` or `useBackButton` fails**: These composables use Vue's `inject()` internally and must be called inside `setup()` or `<script setup>`, not in standalone functions or callbacks.
- **Tab navigation loses state**: Each tab maintains its own stack. Do not use `router.go()` with non-linear (tab-based) routing. Use `useIonRouter().push()` or `router-link` instead.
- **Vue `onMounted` fires only once**: `IonRouterOutlet` caches pages in the DOM. Use `onIonViewWillEnter` instead of `onMounted` for logic that must run every time the page is shown.
- **Content overlaps tab bar or header**: The page component is missing `IonPage` as the root element. `IonPage` sets up the proper flexbox layout.
## Related Skills
- **`ionic-app-development`** — General Ionic development guidance: components, theming, CLI usage.
- **`ionic-app-creation`** — Create a new Ionic app from scratch.
- **`capacitor-vue`** — Capacitor-specific Vue patterns (plugins, hooks, services) without Ionic Framework.
- **`ionic-app-upgrades`** — Upgrade Ionic Framework to a newer version.
© capawesome-team · MIT · capawesome-team/skills