How to Make Plugins for Figma

A look at how to syntax highlight texts

Ferenc Almasi
Nov 17 · 7 min read
Code sample from the Figma plugin
Code sample from the Figma plugin

If you haven’t heard about Figma, it is a UX/UI design application just like Adobe XD or Sketch. It describes itself as “the collaborative interface design tool”. I’ve been using it for a while now, mostly for creating web tips like the one below:

But previously I also used it for it’s main purpose: designing user interfaces. Can you guess what takes up the most time when creating a card similar to the one above? — If you guess highlighting the syntax, you were right. Since I’m using the same colors for the same tokens over and over again, this step is redundant and not automating it is just wasting your time for no reason.

Fortunately, Figma has plugins which could potentially solve this problem. Unfortunately, I haven’t found one so I had to create my own.

The concept

First we need to find out whether it is possible to create such thing, so I went over to the API documentations on Figma and looked into the TextNode object since I wanted to work with text. It looked like you can color a piece of text using the setRangeFills method, so I came to the following conclusion:

setRangeFills expects a start (inclusive) and end (exclusive) index to know what part of the text should be styled. So first we need to get the selected text, get the tokens from it through regex and find out each token’s start and end position then apply the appropriate style to it.

Note that implementing sytnax highlight with regex is not the preferred way. You can’t parse HTML with regex because HTML’s grammar is much more complex than what regex can handle. In order to cope with the task, you would need a full lexer and parser to identifying each token and to make it more robust and act like a real syntax highlighter. However I wanted to get things done with the least amount of effort for a small set of tokens.

Setting up the plugin

In order to start working on plugins, you need to have the desktop application as it can’t be done from the web app. You can download the desktop app here.

Once you have it installed, open it up and go the the “Plugins” option on the left hand side then click on the plus sign on the right hand side next to “Development”. It will present you with the following popup:

Figma — new plugin popup
Figma — new plugin popup

Add a name for your plugin and click on Continue. For the template, we are going to use the default: run once. You can also create an empty project or one with a user interface. It will present you with the following folder structure:

The folder structure
The folder structure

Everything will be done inside the code.ts file. As you can see, Figma uses TypeScript. You can also write your plugin in vanilla JavaScript, in that case you can skip the following steps and go straight to the next section.

If you don’t have TypeScript installed already, you can install it globally with npm i -g typescript. Using Visual Studio Code, run the “Run Build Task” menu item (ctrl+ shift + b on Windows) and select tsc: watch — tsconfig.json to compile the project on each save.

If you’re getting the following error even after installing TypeScript globally:

tsc is not a recognized as an internal or external command...

Try to add the following into your system environment variables:

C:\Users\<user_name>\AppData\Roaming\npm

Styling texts

Initially, code.ts holds a sample plugin, you can delete everything in the file as we are going to build ours from the ground up.

To do some precautions, we will need to check if there is an active selection and if the selection is a text. We can do this with a simple if statement:

The global figma object is exposed by the API. Once we’re sure we have a selection we can get the whole text by accessing the characters node.

To make sure we don’t get type errors we need to cast the current selection as a TextNode. For styling the text, we can call setRangeFills on it which accepts three params:

  • The start index (inclusive)

Making the whole text white can be done with:

As you can see the value of colors can be between 0 and 1 instead of 0 and 255. To make it easier to work with, let’s set up a function for normalizing values.

Normalizing colors

To cap values between 0 and 1, all we have to do is divide each value by 255:

Using this function, I also set up some predefined colors so they can be easily referenced:

This way, we can simply pass colours.x to the setRangeFills function without having to normalize and write out each color. Still, writing out the whole setRangeFills call can be tedious and we may have to color multiple parts of the text with the same color. To make it a little bit more dynamic, let’s also create a function for the setRangeFills calls.

Make it dynamic

Since we may have multiple blocks which require the same coloring, we want our function to accept an array of indexes, more specifically a multidimensional array of indexes where each sub-array holds a start and end index like so:

[[0, 10], [23, 42]]

We might also want to pass the color here. This leaves us with the following function:

We loop through the ranges array and set the start index to index[0] and the end index to index[1]. To try it out, let’s apply a base color to the text with the following function call:

We can essentially, do the same call for everything else. The only problem which prevents us from doing so is to get the proper index for each block of text. We might want a function that returns a multidimensional array, similar to the one above, that holds each and every start and end index.

Getting text ranges

For getting the indexes, I defined the following function, which takes in a text and a regex pattern which should be run against it:

text.matchAll returns a RegExpStringIterator, which can be transformed into an array using the spread operator. For each result, we get the index of the match — this will be the start index — and the length of the result, which will be the end index.

Adding some regex magic

We’ve left with the regex. This is what will get us the indexes for each text block. To keep everything in one place, I added a regex object at the top of the file which is responsible for holding regex for every possible token and language:

As you can see, most of the regex patterns are ending with a lookahead which helps us to match certain pattern but avoid the match in the end result. Unfortunately, lookbehinds are not supported yet so we might have matches where we also select unnecessary parts of the string. For example, the attributeValue will include an equal sign and a quotation mark at the beginning, we don’t want to color them the same way as we do for the attribute value, so we will need to offset the index for some of these patterns.

The way to get around this is to modify the getTextRange function a little bit:

We can introduce a 3rd param called padding. Instead of using textIndex for the start index, we can define a variable that adds padding to textIndex and use that as the start index for the text range. I also added a safe check to avoid going into negative values as we might get errors for that.

Putting everything together

Putting everything together, we can apply different styles with the combination of two function calls: applyStyles and getTextRange:

We supply getTextRange to the applyStyles function with the text and the required regex. For styling attribute values, we can supply an offset of 2, so =" at the beginning of each attribute won’t be styles with the same color.

Lastly, don’t forget to call figma.closePlugin() as the last thing to terminate the plugin.

To try out the plugin, right click on your text, select Plugins — Development, and there you will find it. Now the code block will come alive with the click of a button:

the Figma plugin in action
the Figma plugin in action
Using the highlighter on html markup

Since the writing of this article, a more robust implementation has popped up that uses highlight.js parse logic. You can get the plugin here.

JavaScript in Plain English

Learn the web's most important programming language.

Ferenc Almasi

Written by

Frontend dev with a passion for beautiful code and simple yet attractive designs. Get in touch by saying hello@allma.si or visit allma.si directly 👋

JavaScript in Plain English

Learn the web's most important programming language.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade