Skip to main content
Every FDO plugin is a TypeScript class that extends FDO_SDK and implements FDOInterface. This guide walks you through creating a working plugin from scratch.

Prerequisites

Install the SDK in your project:
npm install @anikitenko/fdo-sdk

Plugin structure

A plugin consists of four required pieces:
  1. A class that extends FDO_SDK and implements FDOInterface
  2. A metadata getter returning a PluginMetadata object
  3. An init() method for setup logic
  4. A render() method that returns an HTML string
import { FDO_SDK, FDOInterface, PluginMetadata } from "@anikitenko/fdo-sdk";

export default class MyPlugin extends FDO_SDK implements FDOInterface {
  private readonly _metadata: PluginMetadata = {
    name: "My Plugin",
    version: "1.0.0",
    author: "Your Name",
    description: "Plugin description",
    icon: "cog"
  };

  get metadata(): PluginMetadata {
    return this._metadata;
  }

  init(): void {
    this.log("MyPlugin initialized!");
  }

  render(): string {
    return `<div>Hello World</div>`;
  }
}

new MyPlugin();
The new MyPlugin() call at the bottom registers the plugin with PluginRegistry. You must include it.

Metadata fields

All five fields are required. The FDO host rejects plugins with missing or invalid metadata.
FieldTypeDescription
namestringDisplay name shown in the FDO UI
versionstringSemver string, e.g. "1.0.0"
authorstringPlugin author name
descriptionstringShort description of the plugin
iconstringA valid BlueprintJS v6 icon name
metadata.icon must be a valid BlueprintJS v6 icon name. FDO uses BlueprintJS v6 in the host application and will reject an unrecognized icon name. You can import isBlueprintV6IconName from the SDK to validate icon names at build time.
You can also set an optional id field to give your plugin a stable storage and logging scope that does not change if you rename the plugin:
private readonly _metadata: PluginMetadata = {
  id: "my-company.my-plugin",
  name: "My Plugin",
  // ...
};

Implementing init()

init() runs once when the plugin loads. Use it to set up state, register IPC handlers, and initialize storage. Keep it fast — do not perform long-running synchronous work here.
init(): void {
  try {
    this.log("Plugin initialized!");
    // Register handlers, set up storage, etc.
  } catch (error) {
    this.error(error as Error);
  }
}

Implementing render()

render() returns the HTML string that FDO mounts inside a sandboxed iframe. It must be synchronous — returning a Promise throws an error.
render(): string {
  return `
    <div style="padding: 20px; font-family: Arial, sans-serif;">
      <h1>Hello from My Plugin</h1>
      <p>This content runs inside a sandboxed iframe.</p>
    </div>
  `;
}
You can also use the SDK’s DOM helper classes to build HTML programmatically instead of writing raw strings:
import { FDO_SDK, FDOInterface, PluginMetadata, DOMText, DOMNested } from "@anikitenko/fdo-sdk";

render(): string {
  const domText = new DOMText();
  const domNested = new DOMNested();

  return domNested.createBlockDiv([
    domText.createHText(1, `Welcome to ${this._metadata.name}`),
    domText.createPText(this._metadata.description),
  ], {
    style: { padding: "20px", fontFamily: "Arial, sans-serif" }
  });
}

Complete working example

The following is a complete plugin based on examples/01-basic-plugin.ts:
import { FDO_SDK, FDOInterface, PluginMetadata, DOMText, DOMNested } from "@anikitenko/fdo-sdk";

export default class BasicPlugin extends FDO_SDK implements FDOInterface {
  private readonly _metadata: PluginMetadata = {
    name: "Basic Plugin Example",
    version: "1.0.0",
    author: "FDO SDK Team",
    description: "A minimal example demonstrating basic plugin creation and lifecycle",
    icon: "cog"
  };

  get metadata(): PluginMetadata {
    return this._metadata;
  }

  init(): void {
    try {
      this.log("BasicPlugin initialized!");
    } catch (error) {
      this.error(error as Error);
    }
  }

  render(): string {
    try {
      const domText = new DOMText();
      const domNested = new DOMNested();

      return domNested.createBlockDiv([
        domText.createHText(1, `Welcome to ${this._metadata.name}`),
        domText.createPText([
          domText.createStrongText("Version:"),
          ` ${this._metadata.version}`
        ].join("")),
        domText.createPText([
          domText.createStrongText("Author:"),
          ` ${this._metadata.author}`
        ].join("")),
        domText.createPText(this._metadata.description),
      ], {
        style: {
          padding: "20px",
          fontFamily: "Arial, sans-serif"
        }
      });
    } catch (error) {
      this.error(error as Error);
      return `<div style="padding: 20px; color: red;"><h2>Error rendering plugin</h2></div>`;
    }
  }
}

new BasicPlugin();

Exporting your plugin

Every plugin file must call new MyPlugin() as its final statement. This registers the instance with the SDK’s PluginRegistry so the FDO host can interact with it.
// At the bottom of your plugin file:
new MyPlugin();
The export default on the class is not required for FDO to load the plugin, but it is conventional and useful for type-checking tools.
The init() and render() methods both have abstract implementations in FDO_SDK that throw if not overridden. TypeScript will warn you at compile time if you forget to implement either method.