Skip to content

jakub-hajduk/figwire

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Figwire

Figwire is a lightweight, TypeScript-friendly library for seamless communication between Figma plugins and their UI.

Inspired by Hono's RPC and figma-await-ipc, Figwire provides a structured way to define APIs for both plugin and UI sides while maintaining strong type safety.

TLDR;

Figwire keeps Figma plugin communication simple, type-safe, and predictable.

  • Define methods using defineApi.
  • Call them from the other side using client<T>.
  • Import from figwire/plugin in plugin code and figwire/ui in ui code.
  • Ensure TypeScript knows about available methods via type exports.

Usage

General idea

Figwire revolves around two core elements:

  • defineApi – Defines methods executed on the plugin (or UI) side.
  • client – A client to call those methods from the opposite side.

The key rule is simple:

  • In UI code, import from figwire/ui.
  • In plugin code, import from figwire/plugin.

Example:

// plugin.ts
import { defineApi, client } from 'figwire/plugin';
// ui.ts
import { defineApi, client } from 'figwire/ui';

Rejections and errors

By throwing an error in api method definition, you can reject the promise when requesting for the response.

// plugin.ts
const pluginApi = defineApi({
  checkAge: (age: number) => {
    if (age > 40) {
      throw new Error(`You're to old to use this feature`)
    }
    
    doCrazyStuff();
  }
});

export type PluginAPI = typeof pluginApi;
// ui.ts
await pluginApiInstance.checkAge(42) // Rejects promise with "You're to old to use this feature" message.

Typings

The client is not inherently aware of the API methods (defineApi).

To enable TypeScript to recognize available methods, we explicitly define and export types from the plugin (or UI) side.

Step 1: Define and export API type in the plugin

// plugin.ts
const pluginApi = defineApi({
  greet: (name: string) => `Hello ${name}!`
});

export type PluginAPI = typeof pluginApi;

Step 2: Import and use the API type in the UI

// ui.ts
import type { PluginAPI } from '../plugin/plugin';
import { client } from 'figwire/ui';

const pluginApiClient = client<PluginAPI>();

This pattern works both ways, so UI can also define an API that the plugin can call.

Examples

Requesting plugin methods from UI

Plugin side

import { defineApi } from 'figwire/plugin';

const pluginApi = defineApi({
  greet: (name: string) => `Hello ${name}!`
});

export type PluginAPI = typeof pluginApi;

UI side

import { client } from 'figwire/ui';
import type { PluginAPI } from './plugin';

(async () => {
  const pluginApiClient = client<PluginAPI>();
  const greeting = await pluginApiClient.greet('Johnny Jeep');

  console.log(greeting); // "Hello Johnny Jeep!"
})();

Requesting UI methods from plugin

UI side

import { defineApi } from 'figwire/ui';

const uiApi = defineApi({
  getInputValue: () => (document.getElementById('username') as HTMLInputElement).value
});

export type UIAPI = typeof uiApi;

Plugin side

import { client } from 'figwire/plugin';
import type { UIAPI } from './ui';

(async () => {
  const uiApiClient = client<UIAPI>();
  const username = await uiApiClient.getInputValue();

  console.log(username);
})();

Full example: bidirectional communication

Plugin side

import { defineApi, client } from 'figwire/plugin';
import type { UIAPI } from './ui';

const pluginApi = defineApi({
  cloneNode: (copies: number) => {
    // Clone a node in Figma
    return { message: 'Node successfully cloned.' };
  }
});

export type PluginAPI = typeof pluginApi;

(async () => {
  const uiApiClient = client<UIAPI>();
  const username = await uiApiClient.getInputValue();

  console.log(username);
})();

UI side

import { defineApi, client } from 'figwire/ui';
import type { PluginAPI } from './plugin';

const uiApi = defineApi({
  getInputValue: () => (document.getElementById('username') as HTMLInputElement).value
});

export type UIAPI = typeof uiApi;

(async () => {
  const pluginApiClient = client<PluginAPI>();

  document.getElementById('button').addEventListener('click', async () => {
    await pluginApiClient.cloneNode(5);
  });
})();

About

Bidirectional IPC communication between UI and core in Figma plugins. Lightweight and typed.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors