capacitor-react
Guides the agent through Capacitor app development with React — project structure, hooks for native features, state management patterns, using Capacitor plugins in React components, and React-specific best practices. Do not use for Ionic Framework with React (use ionic-react), migrating or upgrading Capacitor apps or plugins, or non-React frameworks.
What this skill does
# Capacitor React
Develop Capacitor apps with React — project structure, hooks, state management, and React-specific patterns for accessing native device features.
## Prerequisites
1. **Capacitor 6, 7, or 8** app with React.
2. Node.js and npm installed.
3. React 18 or later.
4. For **iOS**: Xcode installed.
5. For **Android**: Android Studio installed.
## Agent Behavior
- **Auto-detect before asking.** Check the project for `package.json` dependencies (`react`, `react-dom`, `@capacitor/core`), platforms (`android/`, `ios/`), build tools (`vite.config.ts`, `next.config.js`, `webpack.config.js`), 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 the existing code style (functional vs. class components, TypeScript vs. JavaScript, CSS modules vs. styled-components) and generate code that matches.
## Procedures
### Step 1: Analyze the Project
Auto-detect the following by reading project files:
1. **Framework variant**: Check if this is a plain React app (Vite/CRA), Next.js, or Remix by examining `package.json` dependencies and config files (`vite.config.ts`, `next.config.js`, `remix.config.js`).
2. **Capacitor version**: Read `@capacitor/core` version from `package.json`.
3. **React version**: Read `react` version from `package.json`.
4. **TypeScript**: Check if `tsconfig.json` exists and if `.tsx` files are used.
5. **Platforms**: Check which directories exist (`android/`, `ios/`).
6. **Capacitor config format**: Check if the project uses `capacitor.config.ts` or `capacitor.config.json`.
7. **State management**: Check `package.json` for `redux`, `@reduxjs/toolkit`, `zustand`, `jotai`, `@tanstack/react-query`, or similar.
8. **Router**: Check `package.json` for `react-router-dom`, `@tanstack/react-router`, or similar.
### Step 2: Project Structure
A standard Capacitor React project follows this structure:
```
project-root/
├── android/ # Android native project (generated by Capacitor)
├── ios/ # iOS native project (generated by Capacitor)
├── public/
├── src/
│ ├── components/ # Reusable UI components
│ ├── hooks/ # Custom React hooks (including native feature hooks)
│ ├── pages/ # Page/route components
│ ├── services/ # Service modules for Capacitor plugin calls
│ ├── App.tsx # Root component
│ └── main.tsx # Entry point
├── capacitor.config.ts # Capacitor configuration
├── package.json
├── tsconfig.json
└── vite.config.ts # Or other bundler config
```
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: Using Capacitor Plugins in React
Read `references/plugin-usage-patterns.md` for detailed patterns on how to use Capacitor plugins in React components and hooks.
Key principles:
1. **Import plugins directly** — Capacitor plugins are imported as ES modules.
2. **Call plugin methods in event handlers or effects** — never at the module top level.
3. **Use `useEffect` for listeners** — register and clean up Capacitor event listeners inside `useEffect`.
4. **Check platform before calling** — use `Capacitor.isNativePlatform()` or `Capacitor.getPlatform()` to guard platform-specific calls.
### Step 4: Custom Hooks for Native Features
Read `references/custom-hooks.md` for reusable custom hook patterns that wrap Capacitor plugins.
Custom hooks encapsulate native feature access and provide a React-idiomatic API. When the user needs to access a native feature from multiple components, create a custom hook in `src/hooks/` (or wherever the project keeps hooks).
### Step 5: State Management with Native Data
When the project uses a state management library, integrate native data as follows:
1. **React Query / TanStack Query**: Use query functions that call Capacitor plugins. This works well for data that is fetched from native APIs (e.g., device info, contacts, filesystem reads).
2. **Redux / Zustand / Jotai**: Dispatch actions or update atoms from Capacitor plugin callbacks. Keep native API calls in action creators or service modules, not in reducers or stores.
3. **No state library**: Use React context or custom hooks with `useState`/`useReducer` to share native data across components.
Do **not** recommend adding a state management library unless the user's requirements justify it.
### Step 6: Navigation and Deep Links
If the project uses `react-router-dom` or another router:
1. **Deep links**: Register a listener for `appUrlOpen` events from the `@capacitor/app` plugin inside a `useEffect` in the root component or a dedicated hook. Navigate programmatically using the router's `useNavigate()` hook.
```typescript
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { App, URLOpenListenerEvent } from '@capacitor/app';
const useDeepLinks = () => {
const navigate = useNavigate();
useEffect(() => {
const listener = App.addListener('appUrlOpen', (event: URLOpenListenerEvent) => {
const path = new URL(event.url).pathname;
navigate(path);
});
return () => {
listener.then(handle => handle.remove());
};
}, [navigate]);
};
```
2. **Back button handling (Android)**: Register a listener for the `backButton` event from the `@capacitor/app` plugin to handle Android hardware back button presses.
```typescript
import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { App } from '@capacitor/app';
const useBackButton = () => {
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
const listener = App.addListener('backButton', ({ canGoBack }) => {
if (canGoBack) {
navigate(-1);
} else {
App.exitApp();
}
});
return () => {
listener.then(handle => handle.remove());
};
}, [navigate, location]);
};
```
### Step 7: Platform-Specific Rendering
Use `Capacitor.getPlatform()` or `Capacitor.isNativePlatform()` to conditionally render components or apply platform-specific behavior:
```typescript
import { Capacitor } from '@capacitor/core';
const MyComponent: React.FC = () => {
const platform = Capacitor.getPlatform(); // 'ios' | 'android' | 'web'
const isNative = Capacitor.isNativePlatform();
return (
<div>
{platform === 'ios' && <IOSSpecificComponent />}
{platform === 'android' && <AndroidSpecificComponent />}
{!isNative && <WebFallbackComponent />}
</div>
);
};
```
For reusable platform checks, create a utility or hook:
```typescript
import { Capacitor } from '@capacitor/core';
export const usePlatform = () => {
return {
platform: Capacitor.getPlatform(),
isNative: Capacitor.isNativePlatform(),
isIOS: Capacitor.getPlatform() === 'ios',
isAndroid: Capacitor.getPlatform() === 'android',
isWeb: Capacitor.getPlatform() === 'web',
};
};
```
### Step 8: Lifecycle and App State
Use the `@capacitor/app` plugin to respond to app lifecycle events in React:
```typescript
import { useEffect } from 'react';
import { App } from '@capacitor/app';
const useAppState = (onResume?: () => void, onPause?: () => void) => {
useEffect(() => {
const resumeListener = App.addListener('resume', () => {
onResume?.();
});
const pauseListener = App.addListener('pause', () => {
onPause?.();
});
return () => {
resumeListener.then(handle => handle.remove());
pauseListener.then(handle => handle.remove());
};
}, [onResume, onPause]);
};
```
Use this to refresh data when the app returns to the foreground, pause media, or save state when the app is backgrounded.
### Step 9: Build and Run
After iRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.