ProjectDesign Automation · 2026
Fill Data Figma Plugin
A Figma plugin that fills selected vehicle-listing layers from live Google Sheets inventory, replacing copy-paste across dozens of near-identical mockups.
- Figma Plugin API
- TypeScript
- Google Sheets
# selection: 8 text layers, field = fullnameLoaded 8 vehicle entries from Google Sheeteach selected layer takes the next row in order2020 Honda Accord EX2019 Toyota Corolla LE2021 Ford Mustang GT PremiumUpdated 8 layers, nothing outside the selection touched# then 1 rectangle selected, angle front, ratio 16:9Image inserted successfully!Design Automation · Project
Fill Data is a Figma plugin that populates vehicle listing mockups from a public Google Sheet. A designer selects the layers to fill, picks a field or an image angle, and the plugin writes real inventory values into that selection. I built it to replace filling that content in by hand, card by card.
It is one file of plugin code and one HTML pane. The manifest asks for three network domains and nothing else. Everything the plugin does happens against whatever is selected at that moment.
Why real content
Listing layouts only reveal their problems under real content. Real names run long, real prices vary in width, real photos arrive with the wrong crop. Placeholder text hides all of it. Filling a dozen near-identical cards by hand takes long enough that nobody redoes it after a layout change. That is precisely the moment the check matters most. Inventory data already lives in spreadsheets, so a designer can point the plugin at one and edit it without filing a request against anybody.
Filling by selection
There is no layer naming convention to learn. figma.currentPage.selection is the whole input. For a text fill the plugin keeps the TEXT nodes in the selection and writes the chosen field into each one, walking a different vehicle per node. Twelve selected layers become one field across twelve consecutive rows, and currentIndex advances so the next run continues down the list. The field can be a plain column such as price or mileage, or one of two composed values. fullname joins year, make, model and trim, and full_specs builds a four line block. updateTextNode loads the node’s existing font first, falls back to Inter, Roboto or Arial, and restores size, fills, alignment and auto-resize after writing.
Images into frames
An image fill accepts FRAME and RECTANGLE nodes and takes an angle, a processing mode and an aspect ratio. Each sheet row carries eight exterior URLs, at 0, 45, 90 degrees and so on around the car, plus an interior shot. Choosing raw switches to the matching _input column, which holds the unprocessed photograph. A selected rectangle is replaced by a frame in the same place, resized to 16:9, 4:3 or 1:1 around its center, and painted grey under a temporary Loading image... label. The bytes arrive from the UI pane, figma.createImage turns them into a fill, and each step revalidates the frame through figma.getNodeByIdAsync because a designer can delete it mid-fetch.
Choices and their edges
- A public Google Sheet as the source, read through the gviz CSV endpoint. No credentials, no API key and no backend to run. Anyone holding the sheet link holds the data, so this only suits inventory that is already not secret.
- The plugin sandbox cannot reach the network, so every fetch runs in the UI iframe and returns over
postMessage. A CORS proxy sits in the manifest allowlist to make that work. It is a third party in the path of the sheet contents, and it is what I would replace first. - Working from the current selection rather than the document keeps the action scoped and legible. A designer never hands an automation blanket access to a file. It also means the plugin cannot fill a whole page in one command.
- One field per run, spread across the selection. That is predictable, and it composes. A complete card still takes one run per field, which is the price of not inventing a naming convention.
Running the plugin
npm run build compiles code.ts into code.js with tsc, and npm run watch keeps it compiling. Figma imports it from the manifest under Plugins, then Development. On first open the plugin reads sheetConfig out of figma.clientStorage, and with no sheet id it says so rather than failing quietly. Give it a sheet id and a tab name, and it requests that sheet as CSV from the gviz/tq endpoint, parses the rows and reports how many vehicles loaded. Selecting twelve text layers and clicking a field then fills all twelve.
What is missing
The README is still the Figma plugin template’s own setup notes rather than anything about this plugin. package.json carries an empty license field while an MIT LICENSE file sits beside it. A sample CSV is committed to show the expected columns. The manifest declares documentAccess as dynamic-page and allows three domains: the Google Sheets host, the CORS proxy and the media host the image URLs point at. Nothing calls figma.commitUndo, so undoing a twelve layer fill is twelve separate undos. There is no test, and no way to preview a fill before it lands.
Next projectBrowser Automation