Skip to main content

Build Your First UI Extension

Create a plugin that renders an interactive panel inside the Codebolt app. The panel communicates bidirectionally with your plugin backend.

Prerequisites: Codebolt installed and running, Node.js 18+, npm.


Step 1: Create the plugin project

mkdir my-ui-plugin && cd my-ui-plugin
npm init -y
npm install @codebolt/plugin-sdk
npm install -D typescript

Create tsconfig.json:

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src"]
}

Step 2: Configure package.json

Set the codebolt.plugin.ui.path to point to your panel HTML file:

{
"name": "my-ui-plugin",
"version": "1.0.0",
"main": "dist/index.js",
"codebolt": {
"plugin": {
"ui": {
"path": "./ui/default/index.html"
},
"triggers": [{ "type": "manual" }]
}
},
"scripts": {
"build": "tsc"
}
}

The panel ID is derived from the plugin name: plugin-ui-my-ui-plugin.


Step 3: Create the panel HTML

Create the file at ui/default/index.html:

mkdir -p ui/default

This is a standard HTML page. Codebolt injects a global codeboltPlugin object automatically — no imports needed.

Send messages to the backend

Use codeboltPlugin.sendMessage(data):

<script>
document.getElementById('saveBtn').addEventListener('click', () => {
codeboltPlugin.sendMessage({
type: 'save-settings',
apiKey: document.getElementById('apiKey').value,
});
});
</script>

Receive messages from the backend

Use codeboltPlugin.onMessage(handler):

<script>
codeboltPlugin.onMessage((message) => {
if (message.type === 'status') {
document.getElementById('status').textContent = message.status;
}
if (message.type === 'data') {
// Populate form from saved state
document.getElementById('apiKey').value = message.apiKey || '';
}
});
</script>

Request initial state on load

When the panel opens, the plugin may already have saved state. Request it immediately:

<script>
codeboltPlugin.sendMessage({ type: 'getStatus' });
</script>

Use relative asset paths

CSS, JS, and images within your plugin directory are served automatically. Use relative paths:

<link rel="stylesheet" href="./styles.css" />
<script src="./app.js"></script>

Step 4: Create the plugin backend

Create src/index.ts. The backend does three things: listens for panel messages, sends data to the panel, and manages the plugin lifecycle.

Listen for panel messages

import plugin from '@codebolt/plugin-sdk';

const PANEL_ID = 'plugin-ui-my-ui-plugin';

plugin.dynamicPanel.onMessage(PANEL_ID, async (message: any) => {
switch (message.type) {
case 'save-settings':
// Save the settings
await plugin.kvStore.set('my-ui-plugin', 'config', 'settings', message, true);
plugin.dynamicPanel.send(PANEL_ID, { type: 'status', status: 'saved' });
break;

case 'getStatus':
// Load saved settings and send to panel
const result = await plugin.kvStore.get('my-ui-plugin', 'config', 'settings');
const saved = result?.data?.value;
if (saved) {
plugin.dynamicPanel.send(PANEL_ID, { type: 'data', ...saved });
plugin.dynamicPanel.send(PANEL_ID, { type: 'status', status: 'loaded' });
} else {
plugin.dynamicPanel.send(PANEL_ID, { type: 'status', status: 'no config' });
}
break;
}
});

Send data to the panel

plugin.dynamicPanel.send(PANEL_ID, {
type: 'status',
status: 'ready',
});

Plugin lifecycle

plugin.onStart(async (ctx: any) => {
console.log('[MyUIPlugin] Started');
plugin.dynamicPanel.send(PANEL_ID, { type: 'status', status: 'ready' });
});

plugin.onStop(async () => {
console.log('[MyUIPlugin] Stopped');
});

Step 5: Persist state across restarts

Use plugin.kvStore so the panel shows saved data after the plugin restarts:

// Save
await plugin.kvStore.set('my-ui-plugin', 'config', 'settings', data, true);

// Load
const result = await plugin.kvStore.get('my-ui-plugin', 'config', 'settings');
const saved = result?.data?.value;

Send saved data to the panel in onStart or when the panel sends getStatus.


Step 6: Build, install, and test

Build

npm run build

Install locally

Copy the plugin to one of the directories Codebolt scans:

# Option 1: Global plugins (available in all projects)
cp -r my-ui-plugin ~/.codebolt/plugins/my-ui-plugin

# Option 2: Per-project plugins (only this project, overrides global)
cp -r my-ui-plugin <your-project>/.codeboltPlugins/my-ui-plugin

Load and start

  1. Open the Plugins panel in Codebolt.
  2. Click Reload to rescan plugin directories.
  3. Your plugin appears in the list (state: "Loaded").
  4. Click Start to launch the plugin process.
  5. Click the Open button (appears for plugins with a UI) to see your panel.

Development loop

There is no hot-reload. After making code changes:

# 1. Rebuild
npm run build

# 2. In the Codebolt Plugins panel:
# Click Stop → Click Reload → Click Start

For faster iteration, run tsc --watch in one terminal so the build updates automatically whenever you save a file. Then just Stop → Reload → Start in the UI.

REST API (alternative to UI)

You can also manage plugins via HTTP:

# Reload all plugins from disk
curl -X POST http://localhost:2719/plugins/load

# Start your plugin
curl -X POST http://localhost:2719/plugins/my-ui-plugin/start

# Stop your plugin
curl -X POST http://localhost:2719/plugins/my-ui-plugin/stop

# Check plugin status
curl http://localhost:2719/plugins/my-ui-plugin

Where plugins are discovered

Codebolt scans these directories in order:

DirectoryScope
Built-in plugins (shipped with Codebolt)All projects
~/.codebolt/plugins/All projects (global)
<project>/.codeboltPlugins/Current project only (overrides global)

Per-project plugins override global plugins with the same name.


Communication summary

DirectionAPIWhere
Panel to PlugincodeboltPlugin.sendMessage(data)HTML file
Plugin to Panelplugin.dynamicPanel.send(panelId, data)TypeScript backend
Panel listenscodeboltPlugin.onMessage(handler)HTML file
Plugin listensplugin.dynamicPanel.onMessage(panelId, handler)TypeScript backend

All messages are JSON objects. Use a type field to distinguish message kinds.


Project structure

my-ui-plugin/
├── package.json # codebolt.plugin.ui.path points to the HTML
├── tsconfig.json
├── src/
│ └── index.ts # Backend — messaging, persistence, lifecycle
├── ui/
│ └── default/
│ └── index.html # Panel UI — rendered inside Codebolt
└── dist/
└── index.js

See Also