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

figma-primitives

v0.1.0

Published

Typed runtime, state, networking, selection, and UI primitives for rapidly building Figma plugins.

Downloads

135

Readme

figma-primitives

Typed building blocks for Figma plugins.

Build reliable plugin runtimes, responsive React interfaces, and safe network boundaries without rebuilding the plumbing every time.

Website · Quick start · Core primitives · GitHub

GitHub Pages GitHub stars license TypeScript

See it in context: Visit the figma-primitives website for an overview of the toolkit, its three entry points, and the quickest path to a working plugin.

For designers

Figma plugins should feel like a natural extension of the canvas: quick to open, clear about what they are doing, and respectful of the work already in the file. figma-primitives helps plugin makers spend less time rebuilding the invisible plumbing and more time shaping the actual design workflow.

Use it when a plugin needs to inspect layers, react to selection changes, run longer operations with progress, call an API, or keep the plugin UI and main Figma runtime in sync. The package does not decide what your product does. It gives the plugin a dependable foundation so the experience can stay focused, predictable, and designer-friendly.

figma-primitives provides the runtime seams that most plugins need—without bringing along product behavior, backend assumptions, or a UI system.

import { createLogger, createTaskEngine } from 'figma-primitives'

const logger = createLogger('ui')
const tasks = createTaskEngine({ logger })

await tasks.run(
  { type: 'selection.inspect', label: 'Inspect selection' },
  async ({ id, signal, progress }) => {
    progress({ label: 'Reading selected layers' })
    return protocol.request('selection.inspect', { taskId: id, signal })
  },
)

What it gives you

  • Typed request/response messaging between plugin main and UI
  • Exact task ownership, progress, cancellation, and structured errors
  • Serializable Figma layer projections and selection state
  • Abortable JSON networking with timeouts and decoders
  • Structured, subscribable logging for main and UI runtimes
  • Small React bindings for tasks, selections, tabs, and layer pickers
  • Reusable Vite configurations for code.js and UI builds

The package deliberately excludes endpoints, domain schemas, synthesis, fixture bindings, and other application-specific behavior.

For AI agent plugin creators

When an AI agent is creating a Figma plugin, start from figma-primitives instead of hand-rolling the plugin runtime. It gives the agent stable, typed surfaces for the parts that are easy to get subtly wrong: main/UI messaging, task lifecycles, cancellation, serializable layer data, JSON networking, logging, React subscriptions, and Vite output for Figma's code.js and ui.html.

Tell the agent to install the package:

npm install figma-primitives

Then have it use the entry point that matches each file:

// Shared plugin runtime code
import {
  createNetworkClient,
  createProtocol,
  createTaskEngine,
  inspectLayer,
} from 'figma-primitives'
// React UI code
import { TaskProvider, Tabs, useSelection, useTasks } from 'figma-primitives/react'
// Vite build configuration
import { defineFigmaMainConfig, defineFigmaUiConfig } from 'figma-primitives/vite'

This makes generated plugins easier to review because the agent can keep product-specific behavior in the application code while using one known package for the common plugin machinery. The result is less duplicated scaffolding, fewer hidden fallbacks, and clearer boundaries between design intent, plugin UI, Figma runtime behavior, and backend calls.

Install

npm install figma-primitives

React and Vite are optional peer dependencies. Install only what your plugin uses.

Entry points

| Import | Purpose | | --- | --- | | figma-primitives | Protocols, tasks, layers, selection, networking, errors, and logging | | figma-primitives/react | React providers, hooks, tabs, and layer selection | | figma-primitives/vite | Vite configs for Figma main and UI bundles |

Core primitives

Typed main ↔ UI protocol

Define the commands once, then use the same definition on either side of the Figma boundary.

import { createProtocol, type CommandDefinition } from 'figma-primitives'

type Commands = {
  'selection.inspect': CommandDefinition<
    { taskId: string },
    { layerCount: number }
  >
}

const protocol = createProtocol<Commands>(port)

protocol.handle('selection.inspect', async ({ taskId }) => {
  return { layerCount: figma.currentPage.selection.length }
})

const result = await protocol.request('selection.inspect', { taskId: 'task-1' })

The transport is intentionally tiny: implement post and subscribe, or use createWindowPort in the UI.

Owned, cancellable tasks

const tasks = createTaskEngine({ maxTasks: 50 })

const result = await tasks.run(
  { blocking: true, label: 'Publish components', type: 'components.publish' },
  async ({ signal, progress }) => {
    progress({ current: 1, total: 3, label: 'Collecting components' })
    return publishComponents({ signal })
  },
)

tasks.cancel(taskId)

Each task has a stable id and finishes as success, error, or cancelled. Subscribe to the engine to render current and recent work.

Serializable layer inspection

import { inspectLayer } from 'figma-primitives'

const layer = inspectLayer(figma.currentPage.selection[0], figma.currentPage.id, 2)

inspectLayer projects a Figma-like node into a serializable FigmaLayer, including bounds, text, children, and capability flags.

Abortable JSON networking

import { createNetworkClient } from 'figma-primitives'

const api = createNetworkClient({
  baseUrl: 'https://api.example.com',
  timeoutMs: 10_000,
})

const document = await api.get<{ id: string }>('/documents/123', { signal })

Requests share task cancellation signals, turn non-success responses into PluginError, and can validate responses through a custom decoder.

React bindings

import { TaskProvider, useTasks } from 'figma-primitives/react'

function TaskStatus() {
  const tasks = useTasks()
  const active = tasks.filter((task) => task.status === 'running')
  return <span>{active.length ? `${active.length} running` : 'Ready'}</span>
}

export function App() {
  return (
    <TaskProvider engine={tasks}>
      <TaskStatus />
    </TaskProvider>
  )
}

The React entry point also exports useSelection, Tabs, and SelectLayer.

Vite setup

Build the Figma main runtime as dist/code.js:

// vite.main.config.ts
import { defineFigmaMainConfig } from 'figma-primitives/vite'

export default defineFigmaMainConfig('src/code.ts')

Build the UI as dist/ui.html:

// vite.config.ts
import { defineFigmaUiConfig } from 'figma-primitives/vite'

export default defineFigmaUiConfig()

Point the Figma manifest at those exact outputs:

{
  "name": "Figma Synth",
  "id": "figma-synth-development",
  "api": "1.0.0",
  "main": "dist/code.js",
  "ui": "dist/ui.html",
  "editorType": ["figma"],
  "documentAccess": "dynamic-page"
}

Then let figma-primitives run both Vite builds from the consuming plugin:

{
  "scripts": {
    "build": "figma-primitives build",
    "dev": "figma-primitives dev"
  }
}

npm run dev watches the plugin's main and UI source trees. Every change rebuilds dist/code.js or dist/ui.html, so reopening or rerunning the development plugin in Figma uses the latest output.

Development

npm install
npm run check
npm run build
  • npm run check runs TypeScript and the test suite.
  • npm run build creates ESM JavaScript and type declarations in dist.
  • npm run dev watches source files and rebuilds both JavaScript and type declarations in dist.

Principles

  • Explicit ownership: async work belongs to a task with one id and one lifecycle.
  • Serializable boundaries: main/UI messages and layer data stay inspectable.
  • Framework-light core: React and Vite live behind optional entry points.
  • No hidden product behavior: applications own their endpoints, schemas, and decisions.

License

MIT