JSON to Figma plugin. from Vanilla JS to React

Pavel Laptev
Published in
8 min readApr 25, 2020


Not so while ago, I wrote a plugin that can parse a JSON file and populate Figma layers.

I wrote the first 1.0 version of the plugin as MVP. That’s why the plugin was written in vanilla JS and plain HTML and it was pretty hard to refactor it. When I started to write it, I’ve encountered some structuring difficulties — everything was in one file, the inability to use external libraries, and other horrible things that we forgot in modern development.

I decided that if I want to maintain it and evolve the plugin I need a better way — keep files separately, use external solutions, SASS instead of plain CSS, reusable components, in other words, something that I can easily refactor. Plus Figma updated their API since then, with a few new things like relaunch buttons that I wanted to try.

In this article, I want to show not everything and not in chronological order but general set up and methods that I used to rewrite my plugin from vanilla JS to React.

This boilerplate works on Webpack and already includes SVG import module. But it can’t work with SASS/SCSS files and CSS modules.

Adding SASS/SCSS files support

To add SCSS and CSS modules support, you will need to change these files:

Also, create declaration.d.ts file to add SCSS modules support:

And make sure that your compilerOptions in tsconfig.json looks like this:

Now we’re ready to use CSS modules and SCSS files.

Structuring files

All we’re looking for here is src folder 📁. It contents two general folders — app 📁 and plugin 📁 folder.

app folder 📁

This is a UI folder. This folder contains everything related to the interface.

assets folder 📁. All graphic stuff (icons, logos) that we want to use as images in the plugin (but not only, you can store here different types of files).

components 📁. It contains not only basic components like buttons and radio buttons but also sections and views.

context folder 📁 contains only one context so far that we will share across plugin components:

ViewContext.tsx 📄. We will push the fetched JSON file in this context and if the value of the context not equals null we switch the view from the launch screen to the options screen.

elements folder 📁 contains all basic, general components — building bricks like button, radio, checkbox, Icon component, etc.

section folder 📁 should contain all basic plugin sections. I created this folder for the future that’s why it has so far only on section — SectionWrapper with basic paddings, margins, and props like a title for each section.

views folder 📁 contains two main screens — the launch screen LaunchView where we choose how we want to upload a JSON file and OptionsView where we choose how we want to populate layers. And we go to the second (OptionView) screen only if a JSON file was successfully uploaded.

In OperationView we break each section into separate components and share the data like the selected populate option or selected random option using useState(), update, and pushing these states into components.

styles folder 📁. It contains basic styles for the whole plugin. Figma folder contains Figma styles that I took from this repo.

utils folder 📁. In this folder, we store all reusable functions, functions that can be used without context, like a function to show messages showMsg.tsx, or compare strings compareStrings.tsx and shuffle items in an array shuffleArray.tsx etc.

plugin folder 📁

Folder contains functions performed on the API side.

data folder 📁. Here we store data refers to the plugin mostly but we can also share it across the whole plugin. Right now we store the plugin size here. We use plugins’ size variables in several places.

utils folder 📁. To make our controller.tsx file shorter and more readable we store functions like populateByName, populateByTemplateStrings and populateOnlySelected in the utils folder outside the controller.tsx.

App scheme

Highlighted functions

To make this article shorter (as I promised in the beginning 😊) I’ll explain only the most interesting functions and methods that I wrote for the plugin and didn’t mention in my previous article the rest part you can discover by yourself on the Github repo.

`showMsg(type, text)`

In my previous version, I showed messages using alarm() method but, actually, Figma has its own method to show notification messages figma.notify(text, {props}).

UI side
controller.tsx — API side

`flattenObj(obj, path)`

This function is helping us to flat JSON objects. I tried to use external npm libraries, but could not modify them and add my own adjustments.

And from this obj:

we will have this:

I think I found this function here on stackoverflow.

Populate functions

I also refactored “populate” functions and move them to the utils folder to keep the main file clear and readable.

In my previous version, I checked if JSON has nested layers inside these functions which were very inconvenient and hard to support because it was a recursive function. Also in the first plugins’ version, I checked only the first three nested levels, because to me it was too much to handle double recursion in the one function. But in the new function, I pass an object that was already flatted by flattenObj() function.

I take layers (or frames or groups) that our user selected and then I’m going through it and if clicked button name is equal to a layer name and it is a text layer we replace its text:

☝️ We take the JSON then select the next object in the array by newItem which is we increase on every loop and then we take a value from this object by btnName then just in case, we convert it to string.

The rest two functions have the same working principle.

`Populate All Matches` button

One of the important updates in version 2.5 was Populate all mathces button. Using this button you can populate all layers that match the keys of your JSON file at once. No need to click on each button (like Job, Phone, Birth etc.) separately. Just select groups/frames or layers you want to populate and click on the button.

And actually this is not a too complicated method, but pretty simple actually. All we need to know is when Populate all matches button was pressed and then take the array of buttons names and execute for each item populateByName() or populateByTemplateString() functions.

I also added a few additional methods to check if we receive a plain obj or array of objects and additional functions to group objects in an array.

Test Performance

Because we’re dealing withfetch() function I wanted to know how much time the plugin will spend to fetch a JSON file with 5000 items inside.

To do so, open console → go to Network → and choose from the throttling select Slow 3G . Then to prevent caching JSON files, activate Disable cache checkbox. Now you’re ready to test your plugin performance.

In conclusion

It was much easier to write the plugin with React — now I have readable code, structure, and abilities to include external libraries and solutions.


Also, I already added a few new improvements and fixes to the upcoming realize but not too much so far to publish it. And if you have any suggestions, bugs that you can share with me — feel free to reach me out 😎

Thank you for reading!