Skip to main content

Figma-connect - A Typesafe Wrapper around Plugin (sandbox) and UI (iframe) Communication


BennoDev

Hey everyone,

I’m working on a Figma Plugin and found it challenging to communicate between the UI (iframe) and Plugin (sandbox) layer in a typesafe way. So, I created figma-connect, a lightweight wrapper to streamline this communication process.

Would appreciate your thoughts! Thanks 🙂

Example Usage

shared.ts

import { type TFromPluginMessageEvent } from 'figma-connect/plugin';
import { type TFromAppMessageEvent } from 'figma-connect/app';

// Plugin Events (Plugin -> App)
interface TOnSelectNodeEvent extends TFromPluginMessageEvent {
    key: 'on-select-node';
    args: { selected: Pick<SceneNode, 'name' | 'id'>[] };
}

interface TOnDeselectNodeEvent extends TFromPluginMessageEvent {
    key: 'on-deselect-node';
    args: { deselected: Pick<SceneNode, 'name' | 'id'>[] };
}

type TFromPluginMessageEvents = TOnSelectNodeEvent | TOnDeselectNodeEvent;

// App Events (App -> Plugin)
interface TOnUIRouteChangeEvent extends TFromAppMessageEvent {
    key: 'on-ui-route-change';
    args: {
        activeRoute: 'a' | 'b' | 'c';
    };
}

interface TOnUserLoginEvent extends TFromAppMessageEvent {
    key: 'on-user-login';
    args: {
        userId: string;
        timestamp: number;
    };
}

type TFromAppMessageEvents = TOnUIRouteChangeEvent | TOnUserLoginEvent;

app.ts (or ui.ts)

import { FigmaAppHandler } from 'figma-connect/app';
import { TFromPluginMessageEvents, TFromAppMessageEvents } from './shared';

// Create App Handler and pass global 'parent' instance as first argument
const appHandler = new FigmaAppHandler<TFromPluginMessageEvents, TFromAppMessageEvents>(parent);

// Send Events to the 'plugin' part
appHandler.post('on-ui-route-change', { activeRoute: 'a' });
appHandler.post('on-user-login', { userId: 'user123', timestamp: Date.now() });

// Register callbacks to receive Events from the 'plugin' part
appHandler.register({
    key: 'on-select-node',
    type: 'plugin.message',
    callback: async (_, args) => {
        console.log('Selected Nodes:', args.selected);
    },
});
appHandler.register({
    key: 'on-deselect-node',
    type: 'plugin.message',
    callback: async (_, args) => {
        console.log('Deselected Nodes:', args.deselected);
    },
});

plugin.ts (or code.ts)

import { FigmaPluginHandler } from 'figma-connect/plugin';
import { TFromAppMessageEvents, TFromPluginMessageEvents } from './shared';

// Create Plugin Handler and pass global 'figma' instance as first argument
const pluginHandler = new FigmaPluginHandler<TFromAppMessageEvents, TFromPluginMessageEvents>(figma);

// Send Events to the 'app/ui' part
pluginHandler.post('on-select-node', { selected: [{ id: '1v1', name: 'Frame1' }] });
pluginHandler.post('on-deselect-node', { deselected: [{ id: '1v1', name: 'Frame1' }] });

// Register callbacks to receive Events from the 'app/ui' part
pluginHandler.register({
    key: 'on-ui-route-change',
    type: 'app.message',
    callback: async (_, args) => {
        console.log('UI Route Changed:', args.activeRoute);
    },
});
pluginHandler.register({
    key: 'on-user-login',
    type: 'app.message',
    callback: async (_, args) => {
        console.log('User Logged In:', args.userId, 'at', args.timestamp);
    },
});

Source: monorepo/apps/figma-plugin at develop · dyn-art/monorepo · GitHub

by inbeta.group and dyn.art

0 replies

Be the first to reply!

Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings