npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

figwire

v0.0.3

Published

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

Readme

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);
  });
})();