Figma Plugins

Figma Plugins

  • Introduction
  • API Reference
  • Updates

›Basics of Plugins

Getting Started

  • Introduction
  • Prerequisites
  • Setup Guide
  • Figma and FigJam plugins
  • Setting editor type

Basics of Plugins

  • How Plugins Run
  • Accessing the Document
  • Editing Properties
  • TypeScript
  • Plugin Manifest
  • Creating a User Interface
  • CSS Variables and Theming
  • Accepting Parameters as Input

Development Guides

  • Making Network Requests
  • Working with Images
  • Working with Text
  • OAuth with Plugins
  • Debugging
  • Frozen Plugins

Properties

  • ~

Using External Resources

  • Libraries and bundling
  • Build Scripts
  • Resource Links
  • Figma Components

Other

  • Publishing
  • Stability and Updates
  • Proposed API
  • Samples
  • Get Help
  • Known Issues

TypeScript

In manifest.json, the main field always takes a JavaScript file. After all, plugins run in the browser, and browsers only support JavaScript.

You're free to use plain JavaScript, or any of the languages that can be translated into JavaScript. However, to build a quality plugin that won't crash in unexpected situations, we strongly recommend the use of TypeScript.

What is TypeScript?

TypeScript is not a new language. It is an extension of the JavaScript language. You can write JavaScript code, paste it into a TypeScript file. Congratulations, you've just written TypeScript!

TypeScript allows you to add type annotations to your code. These don't change how your code runs, they are just notes for yourself and for the compiler

// The ': string[]' is an annotation
let list: string[] = []
for (const node of figma.currentPage.selection) {
  list.push(node.type)
}

// The ': number' are also annotations
function doThing(x: number, str: string) {
  // ...
}

In addition, we provide type annotations for the entire Figma API. It looks like this:

interface PluginAPI {
  readonly command: string
  readonly root: DocumentNode
  readonly timer?: TimerAPI
  readonly viewport: ViewportAPI
  closePlugin(message?: string): void

  showUI(html: string, options?: ShowUIOptions): void
  readonly ui: UIAPI

  readonly clientStorage: ClientStorageAPI

  getNodeById(id: string): BaseNode | null
  getStyleById(id: string): BaseStyle | null
  ...

Paired with an editor like Visual Studio Code, the editor is smart about knowing what variable has which properties, while you are typing!

These type annotations are more than just comments with a new syntax. The TypeScript compiler can use them to detect accidental errors.

let list: string[] = []
for (const node of figma.currentPage.selection) {
  // Error! Type SceneNode is not assignable to type 'string'
  list.push(node)
}

Having the compiler tell you about errors generally allows you to develop faster. It's much easier to catch and fix errors when the compiler tells you exactly what line is wrong, whereas having to test the plugin to find bugs (or worse, having your users test the plugin) is much more expensive. TypeScript doesn't prevent all bugs from happening of course, but it does eliminate a large class of them.

How do I use the Figma API with TypeScript?

One of the main reasons we want you to use TypeScript is that there are many node types in Figma, and all of them are slightly different. For example, it's easy to forget nodes like boolean operations or slices. And it's possible your plugin doesn't care about them, which is fine, but it shouldn't crash if it runs into one!

Consider, for example, this function which turns a frame into a component:

function turnFrameIntoComponent() {
  const selection: SceneNode = figma.currentPage.selection[0]
  if (!selection) { return }
  const component = figma.createComponent()
  component.x = selection.x
  component.y = selection.y
  component.resize(selection.width, selection.height)
  // Copy children into new node
  for (const child of selection.children) {
    component.appendChild(child)
  }
  selection.remove()
}

This code could crash. The user might not necessarily have a frame selected when running the plugin! TypeScript will tell you that.

The correct thing to do is to check the type property of a node before using it.

function turnFrameIntoComponent() {
  const selection: SceneNode = figma.currentPage.selection[0]
  if (!selection) { return }
  if (selection.type !== 'FRAME') { return } // <----
  const component = figma.createComponent()
  component.x = selection.x
  component.y = selection.y
  component.resize(selection.width, selection.height)
  // Copy children into new node
  for (const child of selection.children) {
    component.appendChild(child)
  }
  selection.remove()
}

And the compiler is smart enough that, past that point, selection is always a FrameNode which always has a children property. It knows to narrow down SceneNode to FrameNode. Just hover over selection to see!

TypeScript also supports type unions. In this example, NodeWithChildren is one of four node types.

type NodeWithChildren = FrameNode | ComponentNode | InstanceNode | BooleanOperationNode

You can combine multiple of these checks together. For example, if you often find yourself needing to check that a node has the children property, you can write a helper function for that. This function's return value is a type predicate, which tells the compiler that if supportsChildren evaluates to true, then the argument must necessarily be of type (FrameNode | ComponentNode | InstanceNode | BooleanOperationNode).

function supportsChildren(node: SceneNode):
  node is FrameNode | ComponentNode | InstanceNode | BooleanOperationNode
{
  return node.type === 'FRAME' || node.type === 'GROUP' ||
         node.type === 'COMPONENT' || node.type === 'INSTANCE' ||
         node.type === 'BOOLEAN_OPERATION'
}

const selection = figma.currentPage.selection[0]
if (supportsChildren(selection)) {
  // Inside this if statement, selection always has .children property
  console.log(selection.children)
}

Type errors are slowing you down?

In some cases, you know your code is correct, but you can't seem to convince the compiler. One way to tell the compiler something is to use a type cast.

// Prints the number of children. 'as NodeWithChildren'
// tells the compiler you're sure about what you're doing
const selection = figma.currentPage.selection[0]
console((selection as NodeWithChildren).children)

In even more extreme measures, you can use the as any cast to have TypeScript not type-check a particular variable at all. It's reasonable to do this sometimes, particularly when prototyping where you want to move as fast as possible. But try not to leave these type casts around. The compiler sometimes raises false alarms, but it finds bugs pretty often too!

← Editing PropertiesPlugin Manifest →
  • What is TypeScript?
  • How do I use the Figma API with TypeScript?
  • Type errors are slowing you down?
Learn more about Figma