JSON to Figma plugin. from Vanilla JS to React
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.
Let’s begin 👇
Choose React boilerplate
When I started, I knew that there are several Figma-React boilerplates and I don’t have to do everything from the beginning.
Eventually, I choose this one:
But you can also check these:
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})
.
`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.
Feedback
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!