Understanding Figma Plugins

August 15, 2021

This post is part of a series on building Figma plugins. Feel free to jump around and checkout the other sections


Introduction

Before diving into the tutorial on how to build your own Figma plugin. I thought it would be useful to explain a bit about how Figma Plugins work.


Plugins are Web Apps

The first thing to note is that figma plugins are built purely with HTML, CSS, Javascript. From Figma documentation:

In Figma, plugins are written in JavaScript and their UI is created with HTML.

To use browser APIs (e.g. to show UI or to access the network), you will need to create an <iframe> with a <script> tag inside. This can be done with figma.showUI(). Inside of this <iframe>, you can write any HTML/JavaScript and access any browser APIs.

A sample ui.html could look like the following:

<div id="app">
  <h2>Rectangle Creator</h2>
  <p>Count: <input id="count" value="5" /></p>
  <button id="create">Create</button>
</div>
 
<script>
  const button = document.getElementById("create");
 
  button.onclick = () => {
    const textBox = document.getElementById("count");
    const count = parseInt(textBox.value, 10);
 
    parent.postMessage(
      //                       [Look Here]
      { pluginMessage: { type: "create-rectangles", count } },
      "*",
    );
  };
</script>

(source: Figma Plugin Docs — Setup Guide)

One file has special "Figma" access

For performance, we have decided to go with an execution model where plugin code runs on the main thread in a sandbox. The sandbox is a minimal JavaScript environment and does not expose browser APIs.

Most of the code you write for your plugin will use regular javascript and browser API's. However there is one file usually named plugin.js which as access to the Figma Plugin API. You define this special file by declaring it as the main property in your manifest.json file at the root of your plugin.

If you want to learn about all of the different methods available inside of your plugin file you can look at the figma typings file, or refer to the API Overview.

A sample plugin.js file might look like this:

figma.showUI(__html__);
 
figma.ui.onmessage = (msg) => {
  if (msg.type === "create-rectangles") {
    const nodes: SceneNode[] = [];
    for (let i = 0; i < msg.count; i++) {
      const rect = figma.createRectangle();
      rect.x = i * 150;
      rect.fills = [{ type: "SOLID", color: { r: 1, g: 0.5, b: 0 } }];
      figma.currentPage.appendChild(rect);
      nodes.push(rect);
    }
    figma.currentPage.selection = nodes;
    figma.viewport.scrollAndZoomIntoView(nodes);
  }
 
  figma.closePlugin();
};

If you are following along you will notice that both in ui.html and in plugin.js we are sharing the same name in this case create-rectangles. So when you post that message from your ui.html your plugin file will pick that up and execute any code you specific. Usually to edit or modify your figma document.

The good news with the Figma Plugin API is that it is very well documented. Most of the methods available have easy to use names that you can easily imagine what they might do. For example:

figma.createRectangle(): RectangleNode
figma.createLine(): LineNode
figma.createEllipse(): EllipseNode
figma.createPolygon(): PolygonNode
figma.createStar(): StarNode
figma.createVector(): VectorNode
figma.createText(): TextNode
figma.createFrame(): FrameNode
figma.createComponent(): ComponentNode

You can choose your tooling to work fast

Just like building a regular web app there are a million choices for tools/libraries you can use to add into your application. If you are just looking to get started I would recommend trying a few starters. 1, 2 3

Some of the tools which I would recommend would be:

figma-plugin-ds:

Using this library I can make my plugin look like a "native" Figma app. It has most of the buttons, inputs and styles you need to get started. It lacks clear documentation but I found it helpful to create a Codepen so that I could work on editing the styling in realtime.

View The Figma Plugin Starter on Codepen

Webpack

I would also recommend setting up web pack so that you can split up your javascript files and have instant reloading when you make changes.

View the Webpack Guide Here

Typescript

Using typescript is certainly optional when building plugins. However the advantages are that when you are writing your code it saves a lot of time with testing. You can spot errors in your editor or learn about the different functions by simply hovering over a method name in VSCode or your editor of choice.

From now on everything else will be in typescript.

Why use typescript


Using React in your Figma Plugin

For my plugin I ended up using React just because it was easier to build more complex UI's using the patterns I'm familiar with. The only difference with React and plain vanilla JS. Is that inside of your ui.html you'll just set the body to an empty div with a id that you can render to.

The HTML of the Plugin

<div id="root"></div>

The ui.tsx root react application

import React from "react"
import ReactDOM from "react-dom"
 
interface RootState {
  plugin_data: any
}
 
export default class UI extends React.Component<{}, RootState> {
  constructor(props: any) {
    super(props)
    window.onmessage = this.receiveMessage
  }
 
  receiveMessage = (event: any) => {
    if (event.data.pluginMessage.type == "updatePluginData") {
      this.setState({ plugin_data: event.data.pluginMessage.data })
    }
  }
 
  render() {
    const { plugin_data } = this.state
    if (!plugin_data) {
      return <div />
    }
    return (
      <div>
        <h1>Hello Figma!</h1>
      </div>
    )
  }
}
 
ReactDOM.render(<UI />, document.getElementById("root"))

Making API requests

Remember when we talked about just HTML,CSS, Javascript. Same thing goes for making API requests. You can use the browsers native fetch request to make any API requests you need.

Imagining you had a button that fetched github repos in figma you could call

const repo = fetchApiRequest("octcat/hello-world");
 
export function fetchApiRequest(repo: string): Promise<any> {
  return new Promise((resolve, reject) => {
    fetch(`https://api.github.com/repos/${fields.repo}`)
      .then((res) => res.json())
      .then((response) => {
        resolve(response);
      })
      .catch((err) => {
        console.log(err);
        reject(err);
      });
  });
}

Next Up

With some basic knowledge of how Figma plugins work we can now move on to the tutorial. Click below to continue reading and build a "real" figma plugin.