capacitor-plugin-development
Capacitor Plugin Development
Create and maintain Capacitor plugins — scaffolding, native API bridging (iOS/Android), type definitions, testing, and publishing.
Prerequisites
| Requirement | Version |
|---|---|
| Node.js | LTS (18+) |
| npm | 6+ |
| Xcode | 15+ (for iOS) |
| Android Studio | Hedgehog 2023.1.1+ (for Android) |
| Capacitor | 6+ |
Agent Behavior
- Auto-detect before asking. Read
package.json, inspect the directory structure, and infer the plugin's current state before prompting the user. - Guide step-by-step. Walk the user through one phase at a time. Do not present multiple unrelated tasks simultaneously.
- Skip what exists. If the plugin is already scaffolded, skip scaffolding. If the iOS implementation already exists, skip to the next platform.
- Ask which platforms to target. If the user does not specify, implement all three (iOS, Android, Web).
Procedures
Step 1: Determine the Task
Ask the user what they want to do. Common tasks:
- Create a new plugin from scratch — proceed to Step 2.
- Add a new method to an existing plugin — skip to Step 5.
- Add a new platform implementation — skip to the relevant platform step (Step 6, 7, or 8).
- Set up plugin configuration — read
references/plugin-configuration.mdand apply. - Set up plugin hooks — read
references/testing-and-workflow.md(Plugin Hooks section) and apply. - Publish the plugin — skip to Step 10.
If the user's intent is clear from context, skip this question and proceed directly.
Step 2: Scaffold the Plugin
Read references/scaffolding.md and guide the user through generating a new plugin project using the Capacitor plugin generator.
Gather the following from the user (or infer from context):
- Plugin npm package name
- Android package ID
- Plugin class name
- Repository URL
- License
- Description
Run the generator and verify the scaffold with npm run verify.
Step 3: Design the TypeScript API
Read references/designing-api.md and guide the user through defining the plugin interface.
- Ask the user what methods the plugin should expose and what data each method accepts/returns.
- Define the plugin interface and all supporting types in
src/definitions.ts. - Register the plugin in
src/index.tsusingregisterPlugin().
Ensure:
- Every method and property has JSDoc comments with
@sincetags. - Options and result types are defined as separate interfaces.
- String union types are used instead of TypeScript enums.
Step 4: Implement the Web Layer
Read references/web-guide.md and implement the web layer in src/web.ts.
- Extend
WebPluginand implement the plugin interface. - For methods that use Web APIs, implement the browser-based logic.
- For methods that have no web equivalent, throw
this.unimplemented(). - For methods that require a web API not available in all browsers, check for the API's existence and throw
this.unavailable()if missing.
After implementation, build and verify:
npm run build
npm run verify:web
Step 5: Define Method Signatures Across Platforms
Before implementing native code, determine the method types for each plugin method:
| Type | TypeScript | iOS | Android |
|---|---|---|---|
| Returns a value | Promise<T> |
CAPPluginReturnPromise |
@PluginMethod() |
| Returns void | Promise<void> |
CAPPluginReturnNone |
@PluginMethod(returnType = PluginMethod.RETURN_NONE) |
| Callback (repeated) | Promise<string> |
CAPPluginReturnCallback |
@PluginMethod(returnType = PluginMethod.RETURN_CALLBACK) |
Most methods use the "returns a value" type. Use "callback" only for continuous data streams (e.g., geolocation watching).
Step 6: Implement the iOS Plugin
Read references/ios-guide.md and implement the iOS layer.
- Create or update the implementation class (e.g.,
ios/Sources/<ClassName>Plugin/Example.swift) with the platform logic. ExtendNSObjectand mark methods with@objc. - Create or update the plugin class (e.g.,
ios/Sources/<ClassName>Plugin/ExamplePlugin.swift):- Extend
CAPPluginand conform toCAPBridgedPlugin. - Set
identifier,jsName, andpluginMethodsproperties. - Implement each
@objc funcmethod, reading data fromCAPPluginCalland callingresolve(),reject(),unavailable(), orunimplemented().
- Extend
- If the plugin requires third-party iOS dependencies, add them to the
.podspecfile. - If the plugin requires permissions, implement
checkPermissions()andrequestPermissions(), and document the requiredInfo.plistkeys.
After implementation, verify:
npm run verify:ios
Step 7: Implement the Android Plugin
Read references/android-guide.md and implement the Android layer.
- Create or update the implementation class (e.g.,
android/src/main/java/<package-path>/Example.java) with the platform logic. - Create or update the plugin class (e.g.,
android/src/main/java/<package-path>/ExamplePlugin.java):- Extend
Pluginand add@CapacitorPlugin(name = "<JSName>"). - Implement each
@PluginMethodmethod, reading data fromPluginCalland callingresolve(),reject(),unavailable(), orunimplemented().
- Extend
- If the plugin requires third-party Android dependencies, add them to
android/build.gradle. - If the plugin requires permissions:
- Define permission aliases in the
@CapacitorPluginannotation. - Implement
checkPermissions()andrequestPermissions(). - Document which permissions the app developer must add to
AndroidManifest.xml.
- Define permission aliases in the
After implementation, verify:
npm run verify:android
Step 8: Add Events (If Needed)
If the plugin emits events to JavaScript:
- TypeScript: Add
addListener()andremoveAllListeners()methods to the plugin interface insrc/definitions.ts. Define an event interface for each event type. - Web: Call
this.notifyListeners('eventName', data)when the event occurs. - iOS: Call
self.notifyListeners("eventName", data: [...])when the event occurs. Register observers inload()if listening to system notifications. - Android: Call
notifyListeners("eventName", jsObject)when the event occurs. OverridehandleOnConfigurationChanged()or register broadcast receivers as needed.
Ensure the event name string is identical across all platforms and matches the eventName parameter in addListener().
Step 9: Generate Documentation and Verify
- Add JSDoc comments to all methods and interfaces in
src/definitions.tsif not already present. - Generate documentation:
npm run docgen
- Run the full verification:
npm run verify
- Lint and format:
npm run lint
npm run fmt
Step 10: Publish
Read references/publishing.md and guide the user through the publishing process.
- Verify
package.jsonfields are correct (name, version, description, files, capacitor). - Run the pre-publish checklist.
- Publish to npm:
npm publish --access public
Error Handling
npm init @capacitor/plugin@latestfails: Ensure Node.js LTS and npm 6+ are installed. Runnode -vandnpm -vto check.npm run verify:iosfails with "module not found": Runcd ios && pod install --repo-update && cd ..to install CocoaPods dependencies.npm run verify:androidfails with build errors: Openandroid/in Android Studio, sync Gradle, and check for missing dependencies or SDK version mismatches.npm run buildfails with TypeScript errors: Checksrc/definitions.tsfor type mismatches. Ensure the web implementation insrc/web.tscorrectly implements the plugin interface.registerPlugin()name mismatch: The first argument toregisterPlugin()insrc/index.tsmust exactly match thejsNameproperty on iOS and the@CapacitorPlugin(name = "...")value on Android. A mismatch causes the plugin to not load.- iOS: Methods not callable from JavaScript: Ensure all plugin methods are marked with
@objcand listed in thepluginMethodsarray ofCAPBridgedPlugin. - Android: Methods not callable from JavaScript: Ensure all plugin methods have the
@PluginMethod()annotation and arepublic. - Events not received in JavaScript: Verify the event name string is identical in the native
notifyListeners()call and the TypeScriptaddListener()definition. VerifyaddListener()is called before the event fires. - Plugin configuration values not reading: Verify the key names in
getConfig().getString("key")match the keys in the Capacitor config file underplugins.<PluginJSName>. npm run docgenproduces empty output: Ensure JSDoc comments are present on the plugin interface methods insrc/definitions.ts, not on the implementation classes.
Related Skills
capacitor-plugin-spm-support— Add Swift Package Manager support to a Capacitor plugin.capacitor-plugin-upgrades— Upgrade a Capacitor plugin to a newer Capacitor major version.capacitor-plugins— Install and configure existing Capacitor plugins in an app project.