A comprehensive toolkit for developing Figma plugins


Quick start#

Pre-requisites#

Initialize a new plugin from a template#

First:

$ npx --yes -- create-figma-plugin figma-hello-world --yes

(Omit the second --yes flag to specify a plugin template or to customize the initial settings.)

Then:

$ cd figma-hello-world
$ ls -a
.gitignore  .vscode  README.md  node_modules  package-lock.json  package.json  tsconfig.json  src
$ ls src
main.ts

src/main.ts is the main entry point for our plugin:

// src/main.ts

export default function () {
  figma.closePlugin('Hello, World!')
}

In package.json, we’re pointing to src/main.ts on the "main" key under "figma-plugin":

  {
    ...
    "figma-plugin": {
      ...
      "name": "Hello World",
+     "main": "src/main.ts"
    }
  }

See the other configuration options, or learn how to add a UI to a plugin command.

Build the plugin#

In package.json, we also have build and watch scripts set up to invoke the build-figma-plugin CLI:

  {
    ...
    "scripts": {
+     "build": "build-figma-plugin --typecheck --minify",
+     "watch": "build-figma-plugin --typecheck --watch"
    },
    ...
  }

The build-figma-plugin CLI is powered by esbuild, which enables extremely fast, sub-second builds. When the --typecheck flag is specified, your TypeScript code will also be compiled and type-checked by the TypeScript compiler.

To build the plugin:

$ npm run build

This will generate a manifest.json file and a build/ directory containing a JavaScript bundle for the plugin.

To watch for code changes and rebuild the plugin automatically:

$ npm run watch

Install the plugin#

In the Figma desktop app:

  1. Open a Figma document.
  2. Go to PluginsDevelopmentNew Plugin….
  3. Click the Click to choose a manifest.json file box, and select the manifest.json file that was generated.

Debugging#

Use console.log statements to inspect values in your code.

To open the developer console in the Figma desktop app, go to PluginsDevelopmentOpen Console.

Publishing to Figma Community#

Learn how to publish your plugin to Figma Community.

Figma will generate a unique plugin ID for you when you first try to publish your plugin. Copy and paste that ID into your plugin’s "figma-plugin" configuration (under the "id" key), then rebuild the plugin to regenerate its manifest.json file.

See also#


Figma plugin basics#

How a Figma plugin works#

Figma’s plugin execution model#

APIMain contextUI context
Figma plugin API✓ Available (via the figma global object)✗ Not available
JavaScript API✗ Only a subset is available (excludes the DOM, fetch)✓ Available

See that:

  1. There is a concept of a “main context” and a “UI context”.
  2. The availability of the Figma plugin API and JavaScript API differs between the two “contexts”.

Main context#

The entry point of a plugin command is a sandboxed JavaScript environment. We call this the plugin command’s “main context”.

Within this main context:

  1. Our JavaScript code can access and manipulate the contents of the Figma document via the Figma plugin API. The plugin API is made available on the figma global object.
  2. Our JavaScript code can only access a subset of the standard browser JavaScript API. Most notably, this subset excludes both the DOM as well as APIs such as fetch.

UI context#

Showing a UI for a plugin command must be explicitly triggered in the command’s main context. Figma would then display a modal in the Figma editor interface; this modal contains an <iframe> within which which we can render a UI. We call this <iframe> the plugin command’s “UI context”.

Within this UI context:

  1. Our JavaScript code cannot access the Figma plugin API; there is no figma global object.
  2. Our JavaScript code can access the full browser JavaScript API. This includes both the DOM and fetch. Note that the DOM here would be DOM of the <iframe>, not the DOM of the Figma editor.
  3. We can have any arbitrary HTML, CSS, and JavaScript in the <iframe>.

Three common use cases#

Conceptually, the main and UI context would communicate through “message passing”. (In practice, this involves registering event handlers and emitting events.) Message passing between the main and UI context is the only way to utilize parts of the Figma plugin API or JavaScript API that are only available in the opposite context.

The following are three common use cases that we will encounter when developing a Figma plugin, and the steps for making each use case possible within Figma’s plugin execution model.

1. “We want to get data from the Figma document, and show the data in our plugin UI.”#

To accomplish this:

  1. In the main context, call the function to show the UI.
  2. Read the required data off the Figma document in the main context. Pass the data from main context → UI context.
  3. Receive and show the data in the <iframe>.

2. “We want to get data from the user, and use the data in our Figma document.”#

To accomplish this:

  1. In the main context, call the function to show the UI.
  2. Render a form within the <iframe>. When the user clicks a submit button in the form, pass the user input data from UI context → main context.
  3. Receive and use the data in the main context.

3. “We want to get data from an API endpoint, and use the data in our Figma document.”#

To accomplish this:

  1. In the main context, call the function to show the UI.
  2. Pass a request to make an API call from main context → UI context.
  3. Make an API call in the <iframe>. When we receive the data from the API, pass the data from UI context → main context.
  4. Receive and use the data in the main context.

What a Figma plugin can and cannot do#

A Figma plugin can…#

A Figma plugin cannot…#


Configuration#

JSON schema#

Validate the plugin configuration in your package.json file using Create Figma Plugin’s configuration JSON schema.

To enable autocomplete and inline validation of your plugin configuration in Visual Studio Code, create a .vscode/settings.json file containing the following:

{
  "json.schemas": [
    {
      "fileMatch": [
        "package.json"
      ],
      "url": "https://yuanqing.github.io/create-figma-plugin/figma-plugin.json"
    }
  ]
}

Configuration options#

Configure your plugin under the "figma-plugin" key of your package.json file.

"apiVersion"#

(string)

Optional. The version of the Figma plugin API to use. Defaults to "1.0.0".

"id"#

(string)

Required to publish the plugin on Figma Community. The plugin ID. This field can be omitted during development but is required if you want to publish your plugin. Figma will generate a unique plugin ID for you when you first try to publish the plugin; copy and paste that ID here.

"name"#

(string)

Required. The name of the plugin.

"main"#

(string or object)

Required, unless "menu" is specified. Path to the main entry point of the plugin command. The plugin command must be the function set as the default export of the file. To use a particular named export instead, specify an object with the following keys:

Example

{
  "figma-plugin": {
    "id": "806532458729477508",
    "name": "Draw Mask Under Selection",
    "main": "src/main.ts"
  }
}

"ui"#

(string or object)

Optional. Path to the UI implementation of the plugin command (as specified via the sibling "main" key). The UI implementation must be the function set as the default export of the file. To use a particular named export instead, specify an object with the following keys:

Example

{
  "figma-plugin": {
    "id": "767379335945775056",
    "name": "Draw Slice Over Selection",
    "main": "src/main.ts",
    "ui": "src/ui.tsx"
  }
}

Learn how to add a UI to a plugin command.

(array)

Required, unless "main" is specified. An array that specifies the commands shown in the plugin’s sub-menu. Each object in the array has the following keys:

Use a "-" in the array to specify a separator between commands in the sub-menu.

Example

{
  "figma-plugin": {
    "id": "837846252158418235",
    "name": "Flatten Selection to Bitmap",
    "menu": [
      {
        "name": "Flatten Selection to Bitmap",
        "main": "src/flatten-selection-to-bitmap/main.ts"
      },
      "-",
      {
        "name": "Settings",
        "main": "src/settings/main.ts",
        "ui": "src/settings/ui.tsx"
      }
    ]
  }
}

See the recipe for specifying multiple commands in the plugin sub-menu.

"relaunchButtons"#

(object)

Optional. An object that specifies the commands that can be set as relaunch buttons. Each key is a relaunchButtonId. Each value specifies the relaunch button command, and is an object with the following keys:

Example

{
  "figma-plugin": {
    "id": "786286754606650597",
    "name": "Organize Layers",
    "menu": [
      {
        "name": "Organize Layers",
        "main": "src/organize-layers/main.ts",
        "ui": "src/organize-layers/ui.tsx"
      },
      "-",
      {
        "name": "Reset Plugin",
        "main": "src/reset-plugin/main.ts"
      }
    ],
    "relaunchButtons": {
      "organizeLayers": {
        "name": "Organize Layers",
        "main": "src/organize-layers/main.ts",
        "ui": "src/organize-layers/ui.tsx"
      }
    }
  }
}

See the recipe for configuring relaunch buttons.

"enablePrivatePluginApi"#

(boolean)

Optional. Allow use of plugin APIs that are only available to private plugins.

"enableProposedApi"#

(boolean)

Optional. Allow use of Proposed APIs that are only available during development.


UI#

Using the Preact component library#

@create-figma-plugin/ui is a library of production-grade Preact components that replicate the Figma editor’s UI design.

UI components from @create-figma-plugin/ui: Icon, Button, Checkbox, Radio Buttons, Segmented Control, Selectable List, Layer, Textbox, Autocomplete

(See the full library of components in the Storybook.)

To install:

$ npm install @create-figma-plugin/ui preact

Then, include a call to showUI in our plugin command’s main entry point:

// src/main.ts

import { showUI } from '@create-figma-plugin/utilities'

export default function () {
  const options = { width: 240, height: 120 }
  const data = { greeting: 'Hello, World!' }
  showUI(options, data)
}

showUI takes two arguments, and the second data argument is useful for passing some initialising data to the UI.

Next, create a file for our UI (eg. src/ui.tsx):

// src/ui.tsx

import { render, Container, Text, VerticalSpace } from '@create-figma-plugin/ui'
import { h } from 'preact'

function Plugin (props: { greeting: string }) {
  return (
    <Container space='medium'>
      <VerticalSpace space='medium' />
      <Text>{props.greeting}</Text>
      <VerticalSpace space='medium' />
    </Container>
  )
}

export default render(Plugin)

See that:

Finally, in package.json, point to our UI file on the "ui" key:

  {
    ...
    "figma-plugin": {
      ...
      "name": "Hello World",
      "main": "src/main.ts",
+     "ui": "src/ui.tsx"
    }
  }

When we rebuild our plugin and run it, we’ll see:

Figma plugin UI modal containing a “Hello, World”

See the Storybook for the full library of Preact components, and the recipe for passing data between the plugin command’s main and UI contexts.

Using custom CSS#

Out of the box, the build-figma-plugin CLI supports CSS Modules:

/* src/styles.css */

.container {
  background-color: var(--color-silver);
}
// src/ui.tsx

import { render } from '@create-figma-plugin/ui'
import { h } from 'preact'
import styles from './styles.css'

function Plugin () {
  // ...
  return (
    <div class={styles.container}>
      {/* ... */}
    </div>
  )
}

export default render(Plugin)

Refer to the base.css file in @create-figma-plugin/ui for the list of CSS variables that are available for use in your custom CSS.

Using a custom UI library#

To use a custom UI library (instead of @create-figma-plugin/ui), write your plugin command’s UI implementation as follows:

// src/ui.ts

import '@create-figma-plugin/ui/lib/css/base.css'

export default function (rootNode: HTMLElement, data: { greeting: string }) {
  rootNode.innerHTML = `<p>${data.greeting}</p>` //=> <p>Hello, World!</p>
}

The exported function receives two arguments:


Utilities#

@create-figma-plugin/utilities is an extensive library of utility functions for common Figma plugin operations. It is meant to complement the Figma plugin API.

To install:

$ npm install @create-figma-plugin/utilities

The utility functions span the following categories:

Events#

import {
  emit,
  on,
  once
} from '@create-figma-plugin/utilities'

emit<Handler>(name, args)#

Calling emit in the main context invokes the event handler for the matching event name in your UI. Correspondingly, calling emit in your UI invokes the event handler for the matching event name in the main context. All args passed after name will be directly applied on the event handler.

Type parameters

Parameters

Return type

void

on<Handler>(name, handler)#

Registers an event handler for the given event name.

Type parameters

Parameters

Return type

Returns a function for deregistering the handler.

() => void

once<Handler>(name, handler)#

Registers an event handler that will run at most once for the given event name.

Type parameters

Parameters

Return type

Returns a function for deregistering the handler.

() => void

Node#

import {
  areSiblingNodes,
  collapseLayer,
  computeBoundingBox,
  computeMaximumBounds,
  computeSiblingNodes,
  createImagePaint,
  deduplicateNodes,
  getAbsolutePosition,
  getDocumentComponents,
  getParentNode,
  getSceneNodeById,
  getSelectedNodesOrAllNodes,
  insertAfterNode,
  insertBeforeNode,
  isWithinInstanceNode,
  loadFontsAsync,
  setAbsolutePosition,
  setRelaunchButton,
  sortNodesByCanonicalOrder,
  sortNodesByName,
  traverseNode,
  updateNodesSortOrder
} from '@create-figma-plugin/utilities'

areSiblingNodes(nodes)#

Checks if all nodes in nodes are sibling nodes.

Parameters

Return type

Returns true if all nodes in nodes are sibling nodes, else false.

boolean

collapseLayer(node)#

Collapses node and all its child nodes in the layer list.

Parameters

Return type

Returns true if at least one layer in the layer list was collapsed by the function, else false.

boolean

computeBoundingBox(node)#

Computes the coordinates (x, y) and dimensions (width, height) of the smallest bounding box that contains the given node. (Does not account for strokes or effects that could extend beyond the node’s bounding box.)

Parameters

Return type

Returns a Rect.

Rect

computeMaximumBounds(nodes)#

Computes the absolute coordinates of the top-left and bottom-right corners of the smallest bounding box that contains the given nodes. (Does not account for strokes or effects that could extend beyond the nodes’ bounding box.)

Parameters

Return type

Returns an array of two Vector objects, one for the top-left corner and another for the bottom-right corner.

[Vector, Vector]

computeSiblingNodes<Node>(nodes)#

Splits nodes into groups of sibling nodes.

Type parameters

Parameters

Return type

Returns an array of array of sibling SceneNode objects.

Array<Array<Node>>

createImagePaint(bytes)#

Creates an ImagePaint object from the bytes of an image.

Parameters

Return type

ImagePaint

deduplicateNodes<Node>(nodes)#

Deduplicates the nodes in nodes. Does not modify the original nodes array.

Type parameters

Parameters

Return type

Returns a new array of SceneNode objects.

Array<Node>

getAbsolutePosition(node)#

Returns the x and y position of the given node relative to the page.

Parameters

Return type

Returns a Vector.

Vector

getDocumentComponents()#

Returns all the local Components in the current document.

Return type

Array<ComponentNode>

getParentNode(node)#

Returns the parent node of the given node.

Parameters

Return type

Throws an error if node.parent is null, else returns node.parent.

BaseNode & ChildrenMixin

getSceneNodeById<Node>(id)#

Returns SceneNode in the current document with the given id. This is a convenience function that wraps the figma.getNodeById function.

Type parameters

Parameters

Return type

Throws an error if no SceneNode with the given id exists, else returns the node cast to the specified Node type parameter.

Node

getSelectedNodesOrAllNodes()#

Returns the selected nodes, or all the top-level nodes on the current page if no nodes are selected.

Return type

Array<SceneNode>

insertAfterNode(node, referenceNode)#

Inserts node after the referenceNode in the layer list.

Parameters

Return type

void

insertBeforeNode(node, referenceNode)#

Inserts node before the referenceNode in the layer list.

Parameters

Return type

void

isWithinInstanceNode(node)#

Checks if the given node is within an Instance node.

Parameters

Return type

Returns true if the node is within an Instance node, else false.

boolean

loadFontsAsync(nodes)#

Loads the fonts used in all the text nodes within the nodes array. This function must be called before modifying any property of a text node that may cause the rendered text to change.

Parameters

Return type

Promise<void>

setAbsolutePosition(node, vector)#

Moves the node to the given x and y position relative to the page. At least one of x or y must be specified.

Parameters

Return type

void

setRelaunchButton(node, relaunchButtonId [, options])#

Sets a relaunch button on node for the command with the given relaunchButtonId as configured under the "relaunchButtons" key in package.json. description is the text displayed below the relaunch button in the Figma UI. See the recipe for configuring relaunch buttons.

Parameters

Return type

void

sortNodesByCanonicalOrder<Node>(siblingNodes)#

Sorts siblingNodes according to their layer list order. Does not modify the original siblingNodes array.

Type parameters

Parameters

Return type

Returns a new array of SceneNode objects.

Array<Node>

sortNodesByName<Node>(nodes)#

Sorts nodes in alphabetical order. Does not modify the original nodes array.

Type parameters

Parameters

Return type

Returns a new array of SceneNode objects.

Array<Node>

traverseNode(node, processNode [, stopTraversal])#

Traverses node and its child nodes recursively in a depth-first manner, passing each node to the specified processNode callback. Each node is also passed to a stopTraversal function. If you return true in stopTraversal for a particular node, then its child nodes will not be traversed.

Parameters

Return type

void

updateNodesSortOrder(siblingNodes)#

Updates the layer list sort order to follow the sort order of the nodes in the siblingNodes array. Does not modify the original siblingNodes array.

Parameters

Return type

Returns true if the layer list sort order was changed by the function, else false.

boolean

Number#

import {
  evaluateNumericExpression,
  isValidNumericInput
} from '@create-figma-plugin/utilities'

evaluateNumericExpression(value)#

Evaluates the given numeric expression.

Parameters

Return type

Returns the result of evaluating the given expression, else null for an invalid expression.

null | number

isValidNumericInput(value [, options])#

Checks if value is a numeric expression, as input by a user. “Partial” inputs are considered valid. Set options.integersOnly to true to check that the expression contains only integers. options.integersOnly defaults to false if not specified.

Parameters

Return type

Returns true if value is a valid numeric expression, else false.

boolean

Object#

import {
  cloneObject,
  compareObjects,
  compareStringArrays,
  extractAttributes
} from '@create-figma-plugin/utilities'

cloneObject<T>(object)#

Creates a deep copy of the given object.

Type parameters

Parameters

Return type

T

compareObjects(a, b)#

Performs a deep equality comparison of objects a and b.

Parameters

Return type

Returns true if a and b are the same, else false.

boolean

compareStringArrays(a, b)#

Compares the string arrays a and b.

Parameters

Return type

Returns true if a and b are the same, else false.

boolean

extractAttributes<PlainObject, Key>(array, attributes)#

Extracts the specified list of attributes from the given array of plain objects.

Type parameters

Parameters

Return type

Returns an array of plain objects.

Array<Pick<PlainObject, Key>>

Settings#

import {
  loadSettingsAsync,
  saveSettingsAsync
} from '@create-figma-plugin/utilities'

loadSettingsAsync<Settings>(defaultSettings [, settingsKey])#

Loads your plugin’s settings (stored locally on the user’s computer under the given settingsKey). settingsKey defaults to 'settings'. Values in settings default to an optional defaultSettings object.

Type parameters

Parameters

Return type

Promise<Settings>

saveSettingsAsync<Settings>(settings [, settingsKey])#

Saves the given settings for your plugin (stored locally on the user’s computer under the given settingsKey). settingsKey defaults to 'settings'.

Type parameters

Parameters

Return type

Promise<void>

String#

import {
  formatErrorMessage,
  formatSuccessMessage,
  formatWarningMessage,
  pluralize
} from '@create-figma-plugin/utilities'

formatErrorMessage(message)#

Adds a prefix to the given message.

Parameters

Return type

string

formatSuccessMessage(message)#

Adds a prefix to the given message.

Parameters

Return type

string

formatWarningMessage(message)#

Adds a prefix to the given message.

Parameters

Return type

string

pluralize(number, singular [, plural])#

Returns singular if number is exactly 1, else returns plural. plural defaults to ${singular}s if not specified.

Parameters

Return type

string

UI#

import {
  showUI
} from '@create-figma-plugin/utilities'

showUI<Data>(options [, data])#

Renders the UI correponding to the command in a modal within the Figma UI. Specify the width, height, and visibility of the UI via options. Optionally pass on some initialising data from the command to the UI. See how to add a UI to a plugin command.

Type parameters

Parameters

Return type

void

Recipes#

Passing data between the plugin command’s main and UI contexts#

The @create-figma-plugin/utilities library includes 3 functions to facilitate data passing (in both directions) between our plugin command’s main and UI contexts:

Consider a toy example:

// src/main.ts

import {
  once,
  // ...
} from '@create-figma-plugin/utilities'

export default function () {
  function handleSubmit (data) {
    console.log(data) //=> { greeting: 'Hello, World!' }
  }
  once('SUBMIT', handleSubmit)
  // ...
}
// src/ui.tsx

import { render, Button } from '@create-figma-plugin/ui'
import {
  emit,
  // ...
} from '@create-figma-plugin/utilities'
import { h } from 'preact'

export default render(Plugin)

function Plugin () {
  // ...
  function handleClick () {
    const data = { greeting: 'Hello, World!' }
    emit('SUBMIT', data)
  }
  return (
    // ...
    <Button onClick={handleClick}>Submit</Button>
    // ...
  )
}

See that:

Specifying multiple commands in the plugin sub-menu#

Commands are specified on the "menu" key under "figma-plugin":

  {
    "figma-plugin": {
      "id": "837846252158418235",
      "name": "Flatten Selection to Bitmap",
+     "menu": [
+       {
+         "name": "Flatten Selection to Bitmap",
+         "main": "src/flatten-selection-to-bitmap/main.ts"
+       },
+       "-",
+       {
+         "name": "Settings",
+         "main": "src/settings/main.ts",
+         "ui": "src/settings/ui.tsx"
+       }
+     ]
    }
  }

See that:

The above configuration would result in the following:

“Flatten Selection to Bitmap” plugin sub-menu

See the other configuration options.

Configuring relaunch buttons#

Relaunch buttons are configured on the "relaunchButtons" key under "figma-plugin":

  {
    "figma-plugin": {
      "id": "786286754606650597",
      "name": "Organize Layers",
      "menu": [
        {
          "name": "Organize Layers",
          "main": "src/organize-layers/main.ts",
          "ui": "src/organize-layers/ui.tsx"
        },
        "-",
        {
          "name": "Reset Plugin",
          "main": "src/reset-plugin/main.ts"
        }
      ],
+     "relaunchButtons": {
+       "organizeLayers": {
+         "name": "Organize Layers",
+         "main": "src/organize-layers/main.ts",
+         "ui": "src/organize-layers/ui.tsx"
+       }
+     }
    }
  }

See that:

Then, call setRelaunchButton in our plugin command’s main entry point:

// src/organize-layers/main.js

import {
  setRelaunchButton,
  // ...
} from '@create-figma-plugin/utilities'

export default async function () {
  setRelaunchButton(figma.currentPage, 'organizeLayers')
  // ...
}

The second argument passed to setRelaunchButton must be a particular relaunchButtonId as configured on the "relaunchButtons" key of our package.json. In the above example, we’re associating figma.currentPage with the organizeLayers relaunch button command.

This would result in the following:

“Organize Layers” relaunch button

If we want to show additional text below the relaunch button, we can pass a third argument to setRelaunchButton:

setRelaunchButton(
  figma.currentPage,
  'organizeLayers',
  { description: 'Organizes all layers on the page based on layer name' }
)

Create Figma Plugin v1.1.1