Quickstart

Build your first Orbis plugin in 5 minutes

Quickstart

Build your first Orbis plugin in 5 minutes using the Orbis SDK. This tutorial walks you through creating a simple “Hello World” plugin with interactive UI and server-side logic.

What You’ll Build

A plugin that:

  • Displays a greeting message with customizable name
  • Has server-side state management
  • Counts button clicks using API routes
  • Shows dynamic UI updates

Prerequisites

Ensure you have completed the Installation guide and have the Orbis development environment set up.

Step 1: Create the Project

bash
# Create a new Rust library project
cargo new --lib quick-start-plugin
cd quick-start-plugin

Step 2: Configure Cargo.toml

toml
[package]
name = "quick-start-plugin"
version = "1.0.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
orbis-plugin-api = { path = "../../crates/orbis-plugin-api" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[profile.release]
opt-level = "s"        # Optimize for size
lto = true             # Link-time optimization
strip = true           # Strip symbols
codegen-units = 1      # Single codegen unit
panic = "abort"        # Abort on panic

Step 3: Write the Plugin Code

Create src/lib.rs:

rust
use orbis_plugin_api::sdk::prelude::*;
use serde_json::json;

// Initialize the plugin with zero boilerplate
orbis_plugin!();

/// Get personalized greeting
fn get_greeting_impl(ctx: Context) -> Result<Response> {
    // Get name from query parameter or use "World"
    let name = ctx.query_param("name").unwrap_or("World");
    
    log::info!("Greeting requested for: {}", name);
    
    Response::json(&json!({
        "message": format!("Hello, {}!", name),
    }))
}

/// Increment click counter
fn increment_count_impl(_ctx: Context) -> Result<Response> {
    // Get current count
    let count: i32 = state::get("count")?.unwrap_or(0);
    let new_count = count + 1;
    
    // Save new count
    state::set("count", &new_count)?;
    
    log::info!("Count incremented to: {}", new_count);
    
    Response::json(&json!({
        "count": new_count,
        "message": format!("Clicked {} times!", new_count)
    }))
}

// Export handlers for FFI
wrap_handler!(get_greeting, get_greeting_impl);
wrap_handler!(increment_count, increment_count_impl);

Step 4: Create the Manifest

Create manifest.json:

json
{
  "name": "quick-start-plugin",
  "version": "1.0.0",
  "description": "A simple greeting plugin with click counter",
  "author": "You",
  "license": "MIT",
  "min_orbis_version": "1.0.0",

  "wasm_entry": "quick_start_plugin.wasm",

  "routes": [
    {
      "path": "/greeting",
      "method": "GET",
      "handler": "get_greeting"
    },
    {
      "path": "/increment",
      "method": "POST",
      "handler": "increment_count"
    }
  ],

  "pages": [
    {
      "id": "greeting-page",
      "title": "Quick Start",
      "icon": "waves",
      "route": "/hello",

      "state": {
        "name": {
          "type": "string",
          "default": "World"
        },
        "count": {
          "type": "number",
          "default": 0
        },
        "message": {
          "type": "string",
          "default": ""
        }
      },

      "sections": [
        {
          "type": "Container",
          "className": "flex items-center justify-center bg-gradient-to-br from-purple-500 to-pink-500",
          "children": [
            {
              "type": "Card",
              "className": "p-8 shadow-2xl max-w-md w-full",
              "content": {
                "type": "Container",
                "className": "flex flex-col space-y-4",
                "children": [
                  {
                    "type": "Text",
                    "content": "Hello, {{state.name}}!",
                    "className": "text-4xl font-bold text-center mb-2"
                  },
                  {
                    "type": "Text",
                    "content": "Clicks: {{state.count}}",
                    "className": "text-center text-gray-600 mb-6"
                  },
                  {
                    "type": "Field",
                    "fieldType": "text",
                    "placeholder": "Enter your name",
                    "defaultValue": "{{state.name}}",
                    "bindTo": "name"
                  },
                  {
                    "type": "Button",
                    "label": "Click Me!",
                    "className": "w-full",
                    "events": {
                      "on_click": [
                        {
                          "type": "call_api",
                          "api": "plugin.increment_count",
                          "method": "POST",
                          "on_success": [
                            {
                              "type": "update_state",
                              "path": "count",
                              "from": "$response.body.count"
                            },
                            {
                              "type": "show_toast",
                              "message": "{{$response.body.message}}",
                              "level": "success",
                              "title": "Counter Updated",
                              "duration": 3000
                            }
                          ]
                        }
                      ]
                    }
                  }
                ]
              }
            }
          ]
        }
      ]
    }
  ]
}

Step 5: Build the Plugin

bash
# Build WASM binary
cargo build --target wasm32-unknown-unknown --release

# Output will be at:
# target/wasm32-unknown-unknown/release/quick_start_plugin.wasm

Build optimization: The [profile.release] settings reduce binary size from ~2MB to ~200KB.

Step 6: Install the Plugin

bash
# Create the plugin directory
mkdir -p ./orbis/src-tauri/plugins/quick-start-plugin

# Copy WASM binary and manifest
cp target/wasm32-unknown-unknown/release/quick_start_plugin.wasm ./orbis/src-tauri/plugins/quick-start-plugin/
cp manifest.json ./orbis/src-tauri/plugins/quick-start-plugin/

# Or use custom plugin directory
export ORBIS_PLUGINS_DIR=/path/to/plugins
cp target/wasm32-unknown-unknown/release/quick_start_plugin.wasm $ORBIS_PLUGINS_DIR/quick-start-plugin/
cp manifest.json $ORBIS_PLUGINS_DIR/quick-start-plugin/

Step 7: Run Orbis

bash
# From the orbis project root
cd orbis
bun run tauri dev

Navigate to the “Quick Start” page in the sidebar. You should see:

  • A greeting with your custom name
  • An input to change the name
  • A button that increments the counter
  • Toast notifications on each click

Understanding the Code

Zero-Boilerplate Initialization

The orbis_plugin!() macro handles all FFI (Foreign Function Interface) setup:

rust
orbis_plugin!();  // Expands to:
// - init() function
// - allocate/deallocate memory management
// - execute() dispatcher
// - All extern declarations for host functions

Type-Safe Handler Wrapping

The wrap_handler! macro eliminates manual FFI:

rust
pub fn get_greeting_impl(ctx: Context) -> Result<Response> {
    // Your business logic here
}
wrap_handler!(get_greeting, get_greeting_impl);

// Expands to FFI-compatible export with:
// - Request parsing from memory
// - Error handling
// - Response serialization

Context API

The Context provides request data:

rust
ctx.query_param("name")         // Get query parameter
ctx.query_param_as::<i32>("id") // Parse as type
ctx.body_as::<MyStruct>()       // Deserialize body

Response Builders

Convenient response creation:

rust
Response::json(&data)                    // JSON response
Response::text("Hello")                  // Plain text
Response::new(200, "OK", json!({}))     // Custom response

State Management

Type-safe persistent state:

rust
state::get::<i32>("count")      // Get typed value
state::set("count", 42)         // Set value
state::delete("count")          // Remove value

Next Steps

Now that you’ve built your first plugin, explore:

Plugin Development

SDK Features

UI Development

Complete Example

For a production-ready example with database access and HTTP requests, see the TODO Plugin in the repository.