Chapter 11 of 12 ~22 min read

Custom Native Plugin

Side-by-side Cordova vs Capacitor — practical migration with working code.


Plugin Structure Comparison

Cordova plugin.xml approach
<!-- plugin.xml — heavyweight config -->
<plugin id="cordova-plugin-mygreet">
  <js-module src="www/MyGreet.js">
    <clobbers target="cordova.plugins.MyGreet"/>
  </js-module>
  <platform name="android">
    <source-file src="src/android/MyGreet.java"
      target-dir="src/com/example"/>
    <config-file target="config.xml">
      <feature name="MyGreet">
        <param name="android-package"
          value="com.example.MyGreet"/>
      </feature>
    </config-file>
  </platform>
</plugin>
Capacitor plugin TypeScript
// Define interface (web + native contract)
export interface MyGreetPlugin {
  greet(options: { name: string }): Promise<{ message: string }>;
}

// Web fallback (browser)
export class MyGreetWeb implements MyGreetPlugin {
  async greet(options: { name: string }) {
    return { message: `Hello, ${options.name} (web)!` };
  }
}

// Register plugin
import { registerPlugin } from '@capacitor/core';
export const MyGreet = registerPlugin<MyGreetPlugin>(
  'MyGreet',
  { web: () => import('./web').then(m => new m.MyGreetWeb()) }
);

Android Native Implementation (Kotlin)

android/src/main/java/.../MyGreetPlugin.kt
import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin
import com.getcapacitor.JSObject

@CapacitorPlugin(name = "MyGreet")
class MyGreetPlugin : Plugin() {

    @PluginMethod
    fun greet(call: PluginCall) {
        val name = call.getString("name") ?: "World"
        val result = JSObject()
        result.put("message", "Hello, $name (Android)!")
        call.resolve(result)
    }
}

Register Plugin in MainActivity

android/.../MainActivity.kt
class MainActivity : BridgeActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        registerPlugin(MyGreetPlugin::class.java)
        super.onCreate(savedInstanceState)
    }
}
Use the official plugin template

Bootstrap a publishable Capacitor plugin with: npm init @capacitor/plugin@latest. It scaffolds TypeScript definitions, Android (Kotlin), iOS (Swift), and web fallback all at once.