A comprehensive toolkit for developing plugins for Figma and FigJam


Quick start#

Pre-requisites#

Initializing a new plugin from a template#

First:

$ npx --yes -- create-figma-plugin my-plugin

(Here, the default template is used. Learn how to use a different plugin template.)

Then:

$ cd my-plugin
$ 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": "My Plugin",
+     "main": "src/main.ts"
    }
  }

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

Building 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

Installing the plugin#

In the Figma desktop app:

  1. Open a Figma or FigJam document.
  2. Go to PluginsDevelopmentImport plugin from manifest….
  3. 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.

Using a different plugin template#

To initialize your plugin with a different plugin template, use the --template flag. For example:

$ npx --yes -- create-figma-plugin my-plugin --template preact-rectangles

Besides the default template, three other templates are available:

See also#


Plugin basics#

How a Figma/FigJam plugin works#

The plugin execution model#

APIMain contextUI context
Figma/FigJam 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/FigJam 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/FigJam 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/FigJam 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/FigJam 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 plugin, and the steps for making each use case possible within the plugin execution model.

1. “We want to get data from the Figma/FigJam 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/FigJam 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/FigJam 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/FigJam 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 plugin can and cannot do#

A plugin can…#

A plugin cannot…#


Configuration#

Configuration options#

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

"apiVersion"#

(string)

Optional. The version of the Figma/FigJam 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.

"editorType"#

(Array<string>)

Optional. For specifying the editor that the plugin is intended for. One of ["figma"], ["figjam"], or ["figma", "figjam"]. Defaults to ["figma"].

Learn how to create a plugin for both Figma and FigJam.

"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.

"parameters"#

(array)

Optional. Defines the list of parameters that the plugin command accepts via Figma’s Quick Action UI. Each parameter is an object with the following keys:

Learn how to accept parameters via the Quick Actions UI in your plugin command.

"parameterOnly"#

(boolean)

Optional. When "parameters" is specified, the default behavior is that the user will be required to input parameters via Figma’s Quick Action UI. Set "parameterOnly" to false to make parameter input optional.

(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",
        "ui": "src/flatten-selection-to-bitmap/ui.ts"
      },
      "-",
      {
        "name": "Settings",
        "main": "src/settings/main.ts",
        "parameters": [
          {
            "key": "resolution",
            "description": "Enter a bitmap resolution"
          }
        ]
      }
    ]
  }
}

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. Set to true to allow the use of plugin APIs that are only available to private plugins.

"enableProposedApi"#

(boolean)

Optional. Set to true to allow the use of Proposed APIs that are only available during development.

JSON schema#

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

If you’re using Visual Studio Code, you can enable autocomplete and inline validation of your plugin configuration by creating a .vscode/settings.json file:

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

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: Button, File Upload Dropzone, Textbox Numeric, Textbox Autocomplete, Checkbox, Selectable Item, Segmented Control

See the full library of components in the Storybook.

To install, do:

$ 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 initializing 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 under "figma-plugin":

  {
    ...
    "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 React#

When building your plugin, the build-figma-plugin CLI will detect and automatically swap out all react and react-dom imports with preact/compat. This means that it’s possible to seamlessly use React components alongside the Preact components from the @create-figma-plugin/ui library.

To use React components in your plugin UI, ensure that react and @types/react are installed:

$ npm install --save-dev react @types/react

For a runnable example, try the react-editor plugin template:

$ npx --yes -- create-figma-plugin my-plugin --template react-editor

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)

Note that class names in your CSS must be written in camel case – eg. .fooBar rather than .foo-bar or .foo_bar.

By default, all the class names in CSS files imported via an import statement (as in the above example) will be hashed. This is to ensure that each class name is globally unique.

To directly “inline” a CSS file in your UI without hashing its class names, add a ! prefix to the import path:

// src/ui.tsx

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

function Plugin () {
  // ...
  return (
    <div class="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

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 plugin operations. It is meant to complement the Figma/FigJam plugin API.

To install:

$ npm install @create-figma-plugin/utilities

The utility functions span the following categories:

When used with the build-figma-plugin CLI, only the functions explicitly imported by your plugin will be included in your plugin bundle(s).

Color#

import {
  convertHexColorToRgbColor,
  convertNamedColorToHexColor,
  convertRgbColorToHexColor,
  isValidHexColor
} from '@create-figma-plugin/utilities'

convertHexColorToRgbColor(hexColor)#

Converts the given hexColor (eg. 000000) to RGB format (eg. { r: 0, g: 0, b: 0 }). Each value in the returned RGB plain object is between 0 and 1.

Parameters

Return type

Returns an RGB plain object, else null if hexColor was invalid.

null | RGB

convertNamedColorToHexColor(namedColor)#

Converts the given namedColor (eg. black) to hexadecimal format (eg. 000000).

Parameters

Return type

Returns a hexadecimal color as an uppercase string, else null if namedColor was invalid.

null | string

convertRgbColorToHexColor(rgbColor)#

Converts the given rgbColor (eg. { r: 0, g: 0, b: 0 }) to hexadecimal format (eg. 000000). Each value in the given RGB plain object must be between 0 and 1.

Parameters

Return type

Returns a hexadecimal color as an uppercase string, else null if rgbColor was invalid.

null | string

isValidHexColor(hexColor)#

Returns true if hexColor is a valid hexadecimal color.

Parameters

Return type

boolean

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.

See the recipe for passing data between the plugin command’s main and UI contexts.

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

Monetization#

import {
  getDocumentUseCount,
  getTotalUseCountAsync,
  incrementDocumentUseCount,
  incrementTotalUseCountAsync,
  resetDocumentUseCount,
  resetTotalUseCountAsync,
  validateGumroadLicenseKeyMainAsync,
  validateGumroadLicenseKeyUiAsync
} from '@create-figma-plugin/utilities'

getDocumentUseCount([key])#

Returns the plugin’s use count for the current document.

Parameters

Return type

number

getTotalUseCountAsync([key])#

Returns the plugin’s total use count.

Parameters

Return type

Promise<number>

incrementDocumentUseCount([key])#

Increments the plugin’s use count for the current document.

Parameters

Return type

Returns the plugin’s new use count for the current document.

number

incrementTotalUseCountAsync([key])#

Increments the plugin’s total use count.

Parameters

Return type

Returns the plugin’s new total use count.

Promise<number>

resetDocumentUseCount([key])#

Resets the plugin’s use count for the current document to 0.

Parameters

Return type

void

resetTotalUseCountAsync([key])#

Resets the plugin’s total use count to 0.

Parameters

Return type

Promise<void>

validateGumroadLicenseKeyMainAsync(options)#

Validates the given Gumroad license key for the product with the given productPermalink in the main context.

Parameters

Return type

Returns a LicenseKeyValidationResult object.

Promise<LicenseKeyValidationResult>

validateGumroadLicenseKeyUiAsync(options)#

Validates the given Gumroad license key for the product with the given productPermalink in the UI context.

Parameters

Return type

Returns a LicenseKeyValidationResult object.

Promise<LicenseKeyValidationResult>

Node#

import {
  areSiblingNodes,
  collapseLayer,
  computeBoundingBox,
  computeMaximumBounds,
  computeSiblingNodes,
  createImagePaint,
  deduplicateNodes,
  getAbsolutePosition,
  getDocumentComponents,
  getParentNode,
  getSceneNodeById,
  getSelectedNodesOrAllNodes,
  insertAfterNode,
  insertBeforeNode,
  isWithinInstanceNode,
  loadFontsAsync,
  setAbsolutePosition,
  setRelaunchButton,
  sortNodesByCanonicalOrder,
  sortNodesByName,
  traverseNode,
  unsetRelaunchButton,
  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 the bounding box as 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)#

Returns the result of deduplicating the nodes in nodes. Does not modify the original nodes array.

Type parameters

Parameters

Return type

Returns a new array of unique 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 the 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 of vector 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. Any relaunch buttons set previously will be retained.

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

unsetRelaunchButton(node [, relaunchButtonId])#

Unsets the relaunch button on node for the command with the given relaunchButtonId. If relaunchButtonId is not specified, unsets all relaunch buttons on node.

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 numeric 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.

Parameters

Return type

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

boolean

Object#

import {
  cloneObject,
  compareObjects,
  compareStringArrays,
  deduplicateArray,
  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

deduplicateArray<T>(array)#

Returns the result of deduplicating the given array. Does not modify the original array.

Type parameters

Parameters

Return type

Returns a new array with unique values.

Array<T>

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).

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).

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 modal’s width, height, title, and whether it is visible via options. Optionally pass on some initialising data from the command to the UI.

Learn 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'

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

export default render(Plugin)

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",
+          "ui": "src/flatten-selection-to-bitmap/ui.ts"
+        },
+        "-",
+        {
+          "name": "Settings",
+          "main": "src/settings/main.ts",
+          "parameters": [
+            {
+              "key": "resolution",
+              "description": "Enter a bitmap resolution"
+            }
+          ]
+        }
+      ]
+    }
    }
  }

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

To show additional text below the relaunch button, pass a third argument to setRelaunchButton:

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

Using image assets in your plugin UI#

Image assets used in your plugin UI must be “inlined” into you plugin’s UI bundle. Consider the following example where a PNG image is used in the UI:

// src/ui.tsx

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

import image from './image.png'

function Plugin () {
  // ...
  return (
    // ...
    <img src={image} />
    // ...
  )
}

export default render(Plugin)

Note that image is a Base64-encoded data URL string of the imported image.png file, so it is set as the src attribute of the img HTML element.

If you’re writing your plugin in TypeScript, you’ll also need to add a .d.ts typings file to your plugin’s src directory containing the following:

// src/image-assets.d.ts

declare module '*.gif' {
  const content: string
  export default content
}
declare module '*.jpg' {
  const content: string
  export default content
}
declare module '*.png' {
  const content: string
  export default content
}
declare module '*.svg' {
  const content: string
  export default content
}

Making the plugin window resizable#

The Figma/FigJam plugin window is not resizable by default; this must be implemented by the plugin itself. In practice, this involves:

  1. Listening to click-and-drag events in the plugin window, and calculating an updated window size based on the mouse position.
  2. Calling figma.ui.resize with the updated window size.

@create-figma-plugin/ui includes a useWindowResize hook that makes it easier to implement a resizable plugin window:

// src/ui.tsx

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

function Plugin () {
  // ...
  function onWindowResize(windowSize: { width: number; height: number }) {
    emit('RESIZE_WINDOW', windowSize)
  }
  useWindowResize(onWindowResize, {
    minWidth: 120,
    minHeight: 120,
    maxWidth: 320,
    maxHeight: 320
  })
  // ...
}

export default render(Plugin)

The hook takes two arguments:

In the main context, we register a handler for the RESIZE_WINDOW event, and invoke figma.ui.resize with the new window size:

// src/main.ts

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

export default function () {
  // ...
  on('RESIZE_WINDOW', function (windowSize: { width: number; height: number }) {
    const { width, height } = windowSize
    figma.ui.resize(width, height)
  })
  // ...
  showUI({
    width: 240,
    height: 240
  })
}

To restrict the resize direction, set options.resizeDirection to either horizontal or vertical:

  useWindowResize(onWindowResize, {
    minWidth: 120,
    maxWidth: 320,
+   resizeDirection: 'horizontal'
  })

The useWindowResize hook also supports toggling the plugin window size on double-clicking the bottom and right edges of the plugin window:

  useWindowResize(onWindowResize, {
    minWidth: 120,
    minHeight: 120,
    maxWidth: 320,
    maxHeight: 320,
+   resizeBehaviorOnDoubleClick: 'minimize'
  })

Setting options.resizeBehaviorOnDoubleClick to minimize means that the plugin window will be set to the minimum size on double-click. Correspondingly, setting it to maximize means that the plugin window will be set to the maximum size on double-click.

For a runnable example, try the preact-resizable plugin template:

$ npx --yes -- create-figma-plugin my-plugin --template preact-resizable

Customizing the build#

Customizing the underlying esbuild configuration#

The build-figma-plugin CLI is powered by the esbuild compiler. To customize the underlying build configuration for the main bundle, create a build-figma-plugin.main.js file:

// build-figma-plugin.main.js

module.exports = function (buildOptions) {
  // ...
  return {
    ...buildOptions,
    // ...
  }
}

buildOptions is the original esbuild configuration object used internally by the build-figma-plugin CLI. The exported function must return the new configuration object to be used.

Correspondingly, use a build-figma-plugin.ui.js file to customize the build configuration for the UI bundle.

Customizing the manifest.json file#

To modify the manifest.json file just before it gets output by the build-figma-plugin CLI, create a build-figma-plugin.manifest.js file as follows:

// build-figma-plugin.manifest.js

module.exports = function (manifest) {
  // ...
  return {
    ...manifest,
    // ...
  }
}

The exported function receives the original manifest.json that’s created by the build-figma-plugin CLI, and must return the new manifest.json plain object to be output.

Create Figma Plugin v1.6.1