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 , 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.md
and 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
.
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 .
- Register the plugin in using .
Ensure:
- Every method and property has JSDoc comments with tags.
- 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
and implement the web layer in
.
- Extend and implement the plugin interface.
- For methods that use Web APIs, implement the browser-based logic.
- For methods that have no web equivalent, throw .
- For methods that require a web API not available in all browsers, check for the API's existence and throw if missing.
After implementation, build and verify:
bash
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 | | | |
| Returns void | | | @PluginMethod(returnType = PluginMethod.RETURN_NONE)
|
| Callback (repeated) | | | @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
and implement the iOS layer.
- Create or update the implementation class (e.g.,
ios/Sources/<ClassName>Plugin/Example.swift
) with the platform logic. Extend and mark methods with .
- Create or update the plugin class (e.g.,
ios/Sources/<ClassName>Plugin/ExamplePlugin.swift
):
- Extend and conform to .
- Set , , and properties.
- Implement each method, reading data from and calling , , , or .
- If the plugin requires third-party iOS dependencies, add them to the file.
- If the plugin requires permissions, implement and , and document the required keys.
After implementation, verify:
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 and add
@CapacitorPlugin(name = "<JSName>")
.
- Implement each method, reading data from and calling , , , or .
- If the plugin requires third-party Android dependencies, add them to .
- If the plugin requires permissions:
- Define permission aliases in the annotation.
- Implement and .
- Document which permissions the app developer must add to .
After implementation, verify:
Step 8: Add Events (If Needed)
If the plugin emits events to JavaScript:
- TypeScript: Add and methods to the plugin interface in . 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 in if listening to system notifications.
- Android: Call
notifyListeners("eventName", jsObject)
when the event occurs. Override handleOnConfigurationChanged()
or register broadcast receivers as needed.
Ensure the event name string is identical across all platforms and matches the
parameter in
.
Step 9: Generate Documentation and Verify
- Add JSDoc comments to all methods and interfaces in if not already present.
- Generate documentation:
- Run the full verification:
- Lint and format:
Step 10: Publish
Read
and guide the user through the publishing process.
- Verify fields are correct (name, version, description, files, capacitor).
- Run the pre-publish checklist.
- Publish to npm:
bash
npm publish --access public
Error Handling
npm init @capacitor/plugin@latest
fails: Ensure Node.js LTS and npm 6+ are installed. Run and to check.
- fails with "module not found": Run
cd ios && pod install --repo-update && cd ..
to install CocoaPods dependencies.
- fails with build errors: Open in Android Studio, sync Gradle, and check for missing dependencies or SDK version mismatches.
- fails with TypeScript errors: Check for type mismatches. Ensure the web implementation in correctly implements the plugin interface.
- name mismatch: The first argument to in must exactly match the property 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 and listed in the array of .
- Android: Methods not callable from JavaScript: Ensure all plugin methods have the annotation and are .
- Events not received in JavaScript: Verify the event name string is identical in the native call and the TypeScript definition. Verify 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 under .
- produces empty output: Ensure JSDoc comments are present on the plugin interface methods in , 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.
- — Install and configure existing Capacitor plugins in an app project.