PixelFlow – Content Sync
Stop copy-pasting between spreadsheets and Figma. Creates a live two-way bridge between Google Sheets and your Figma designs - push content directly into text layers or audit live copy back to your sheet in seconds.
Figma Plugin · Documentation
Your copy, always in sync.
Stop copy-pasting between spreadsheets and Figma. PixelFlow – Content Sync creates a live two-way bridge between Google Sheets and your Figma designs - push content directly into text layers or audit live copy back to your sheet in seconds.
Pull copy from your Google Sheet and push it directly into matching Figma text layers. Supports English (EN) and Localized columns.
Read text from Figma layers and write it back to the Suggested Copy (EN) column. Perfect for content audits.
Setup · Step 1
Create your Google Sheet
The plugin reads from a structured Google Sheet as the single source of truth. Download the sample file - it's pre-built with the correct format, tabs, and colour codes.
1.1 Download the Sample Structure File
- 1
Open the plugin in Figma. On the Setup screen, expand Step 1 - Create your Google Sheet.
- 2
Click ⬇ Download Structure File. A file named
PixelFlow_ContentSync_Sample.xlsxdownloads. - 3
Go to Google Drive → + New → File Upload. Select the downloaded file.
- 4
Double-click once uploaded → choose Open with Google Sheets.
Image: Plugin Step 1 - Download Structure File button
1.2 Sheet Structure
Every tab uses the same 4-column layout.
| Column | Purpose | Example |
|---|---|---|
Col A | Description - what the element is | Cancel Modal Title |
Col B | Unique String Key - must match your Figma layer name exactly | glb_modal_cancel_title |
Col C | Suggested Copy (EN) - the English content value | Are you sure you want to cancel? |
Col D | Localized - translated version for Localized sync mode | Weet u zeker dat u wilt annuleren? |
| Col A - Description | Col B - Key | Col C - EN Copy | Col D - Localized |
|---|---|---|---|
| Uncategorize | |||
| Back Button | glb_btn_back | Back | |
| Cancel Button | glb_btn_modal_cancel | Cancel | |
| Cancel Modal | |||
| Cancel Modal Title | glb_modal_cancel_title | Are you sure? | Weet u zeker? |
1.3 Name your Figma Layers to Match
The value in Column B must exactly match the Figma text layer name. The match is case-insensitive and ignores spaces, hyphens, and underscores.
lp_hero_first_name, LP_HERO_FIRST_NAME, and lp hero first name all match - the plugin normalises before comparing.Setup · Step 2
Add the Apps Script
The plugin communicates with your Sheet through a small Google Apps Script Web App. You deploy it once - it handles all reading and writing.
2.1 Open the Apps Script Editor
- 1
In your Google Sheet, click Extensions → Apps Script. A new tab opens.
- 2
Select all existing code (
Ctrl+A/Cmd+A) and delete it.
2.2 Copy and Paste the Script
- 3
Back in PixelFlow, expand Step 2 - Add the Apps Script.
- 4
Click Copy on the code block. The full script is now on your clipboard.
- 5
Paste into the Apps Script editor and press 💾 Save (
Ctrl+S/Cmd+S).
// PixelFlow Content Sync v4 | Google Apps Script // @OnlyCurrentDoc // // DEPLOY: // 1. Extensions > Apps Script > paste this > Ctrl+S // 2. Deploy > New Deployment > Web App // 3. Execute as: Me | Who has access: Anyone // 4. Copy the Web App URL > paste in PixelFlow plugin // After changes: Deploy > Manage Deployments > Edit > New version var SECTION_BG = "#cfe2f3"; function doGet(e) { try { var action = (e && e.parameter && e.parameter.action) || "read"; if (action === "read" || action === "probe") { return json({ sheets: getAllSheets(), version: "4.0.0" }); } if (action === "write") { var raw = (e.parameter && e.parameter.data) || "{}"; var body = JSON.parse(Utilities.newBlob(Utilities.base64Decode(raw)).getDataAsString()); return json(writeRows(body.rows || [])); } return json({ error: true, message: "Unknown action: " + action }); } catch (err) { return json({ error: true, message: String(err) }); } } function json(obj) { return ContentService.createTextOutput(JSON.stringify(obj)) .setMimeType(ContentService.MimeType.JSON); } function getAllSheets() { var out = {}; SpreadsheetApp.getActiveSpreadsheet().getSheets().forEach(function(sh) { out[sh.getName()] = parseSheet(sh); }); return out; } function parseSheet(sheet) { var lastRow = sheet.getLastRow(); if (lastRow < 2) return []; var lastCol = Math.max(sheet.getLastColumn(), 4); var vals = sheet.getRange(1, 1, lastRow, lastCol).getValues(); var bgs = sheet.getRange(1, 1, lastRow, 1).getBackgrounds(); var hdr = vals[0].map(function(h) { return String(h).toLowerCase().trim(); }); var is5 = hdr.length >= 5 && hdr[2].indexOf("key") > -1; var ki = is5 ? 2 : 1, si = is5 ? 3 : 2, li = is5 ? 4 : 3; var rows = [], section = null; for (var i = 1; i < vals.length; i++) { var row = vals[i]; var bg = String(bgs[i][0]).toLowerCase().trim(); var c0 = String(row[0] || "").trim(); var key = String(row[ki] || "").trim(); var sug = String(row[si] || "").trim(); var loc = String(row[li] || "").trim(); if (!c0 && !key && !sug) continue; var isSec = bg === SECTION_BG || bg.indexOf("cfe2f3") > -1 || (c0 && !key && !sug); if (isSec) { section = c0 || key; rows.push({ isSection: true, section: section }); continue; } if (!key) continue; rows.push({ isSection: false, section: section || "", key: key, description: c0, suggested: sug, localized: loc, rowIdx: i + 1, sugCol: si + 1, locCol: li + 1 }); } return rows; } function writeRows(rows) { var ss = SpreadsheetApp.getActiveSpreadsheet(); var updated = 0, notFound = []; var byTab = {}; rows.forEach(function(r) { var t = r.tab || ""; if (!byTab[t]) byTab[t] = []; byTab[t].push(r); }); Object.keys(byTab).forEach(function(tab) { var sheet = ss.getSheetByName(tab); if (!sheet) { byTab[tab].forEach(function(r) { notFound.push(r.key); }); return; } var parsed = parseSheet(sheet), km = {}; parsed.forEach(function(r) { if (!r.isSection) km[r.key] = r; }); byTab[tab].forEach(function(wr) { var m = km[wr.key]; if (!m) { notFound.push(wr.key); return; } sheet.getRange(m.rowIdx, m.sugCol).setValue(wr.value); updated++; }); }); SpreadsheetApp.flush(); return { updated: updated, notFound: notFound }; }
2.3 Deploy as a Web App
- 6
Click Deploy → New deployment in the Apps Script editor.
- 7
Click the gear ⚙ → select type → Web app.
- 8
Set Execute as: Me and Who has access: Anyone. Click Deploy.
- 9
Authorise when prompted. Copy the Web App URL - you'll paste it in Step 3.
https://script.google.com/macros/s/ABC123xyz…/exec
Image: Apps Script - Deploy as Web App dialog (Execute as: Me, Who has access: Anyone)
Setup · Step 3
Connect the Plugin
With your Web App URL ready, connecting takes under 10 seconds.
- 1
In PixelFlow, expand Step 3 - Connect your Web App URL.
- 2
Paste the Web App URL from Step 2 into the input field.
- 3
Click Connect & Sync. The plugin fetches all tabs and switches to the Sync view.
Connection Error Reference
| Error | Cause | Fix |
|---|---|---|
| HTML page returned | Who has access set to Only myself | Re-deploy with Anyone |
| Network error | URL incorrect or script not deployed | Verify URL, re-deploy |
| Unexpected format | Script code is outdated | Re-copy script from Step 2, deploy new version |
Using the Plugin
The Sync View
Once connected, the Sync view is your main workspace. Here's every element annotated.
| Area | What it does |
|---|---|
| Mode toggle | Switch between Sheet → Figma (blue) and Figma → Sheet (purple) |
| Tab bar | One tab per sheet tab - click to switch between Global, Journey Tab, etc. |
| Search bar | Filter keys by Unique String Key or Description in real time |
| EN / Localized | Choose which column to sync: Col C (EN) or Col D (Localized) |
| Annotate toggle | Overlay key labels on your Figma canvas above matching text layers |
| Page bar | Tab name, selected count, ↻ Fetch button, Collapse/Expand all |
| Key list | Expandable sections. Check keys to include in the next sync |
| Sync bar | Status text + main action button (Sync to Figma or Push to Sheet) |
Primary Workflow
Sheet → Figma
Pull content from your Google Sheet and push it into matching Figma text layers.
- 1
Ensure Sheet → Figma mode is selected (blue).
- 2
Select the tab (e.g.
GlobalorJourney Tab). - 3
Choose language: EN (Col C) or Localized (Col D).
- 4
Check the keys you want to sync. Use Select all to grab everything.
- 5
Click ⬇ Sync to Figma. Sheet data auto-refreshes before syncing.
Match Indicators
A Figma text layer with this exact key name was found on the current page.
No matching Figma layer found. Check the layer is named correctly and on the current page.
This key has a localized value available in Column D.
GIF: Sheet → Figma sync in action - keys being pushed into Figma layers live
Content Audit
Figma → Sheet
Read text from Figma layers and write it back to Col C. Ideal for audits or when copy was edited directly in Figma.
- 1
Click Figma → Sheet in the mode toggle (turns purple).
- 2
Select the keys you want to push back via checkboxes.
- 3
Click ⬆ Push to Sheet.
- 4
A confirmation dialog shows how many rows will be overwritten. Click Yes, Overwrite.
Advanced Features
↻ Fetch - Reload Without Syncing
Added new keys to your Sheet while the plugin is open? Hit Fetch to reload everything without triggering a sync.
Advanced Features
Search - Find Keys Fast
The search bar filters the key list in real time. Results appear as you type.
| Searches | Column | Example |
|---|---|---|
| Unique String Key | Col B | Typing hero matches lp_hero_first_name |
| Description | Col A | Typing cancel matches Cancel Modal Title |
Advanced Features
Annotate - Keys on Canvas
Toggle Annotate ON to overlay small key labels directly above each matching Figma text layer.
- ✓
Annotations appear as a frame named PixelFlow Annotations on your Figma page
- ✓
Toggle OFF instantly removes all labels from the canvas
- ✓
Annotations update automatically after every sync
Image: Mapping Layers UI - Canvas with key label annotations above each matched text layer
Advanced Features
Info Modal - Quick Key Details
Click the ⓘ icon on any key row to open a quick-view modal with all four data columns.
| Field | Column | Notes |
|---|---|---|
| Description | Col A | Human-readable label for the component |
| Unique String Key | Col B | One-click Copy button - paste directly into a Figma layer name |
| Suggested Copy (EN) | Col C | Current English content value |
| Localized | Col D | Translated value, if available |
Advanced Features
Select & Collapse
Select All / Deselect All
The checkbox in the page bar selects or deselects all visible keys. When some (not all) are checked, it shows an indeterminate state (-). The selected count (e.g. 4/12 selected) shows alongside the tab name.
Collapse All / Expand All
The contextual button on the right of the page bar collapses all section accordions for a compact view, or expands all to see every key at once. The label switches automatically based on current state.
Help
Troubleshooting
Common problems and their fixes. Click to expand any item.
Reference
Quick Reference
Sheet Structure
| Item | Detail | Notes |
|---|---|---|
| Global tab | Light-green section rows | Shared components: buttons, modals, nav |
| Journey Tab | Light-blue section rows | Page-specific copy: hero, features, CTA |
Col A | Description | Human-readable label |
Col B | Unique String Key | Must match Figma layer name exactly |
Col C | Suggested Copy (EN) | Synced in EN mode |
Col D | Localized | Synced in Localized mode |
Plugin Controls
| Control | What it does |
|---|---|
| Mode toggle | Blue = Sheet → Figma | Purple = Figma → Sheet |
| EN / Localized | Selects which column is used during sync (Col C or Col D) |
| Annotate toggle | Shows/hides key labels on the Figma canvas |
| ↻ Fetch | Reloads all keys from the Sheet without syncing |
| Select all ☑ | Checks/unchecks all visible keys in the current tab |
| Collapse / Expand all | Toggles all section accordions open or closed |
| ⓘ Info icon | Opens a modal with all four column values for that key |
| ⬇ Sync to Figma | Pushes Sheet content into matching Figma text layers |
| ⬆ Push to Sheet | Reads Figma layer text and writes it to Col C |
| ⚙ Settings icon | Returns to Setup screen to change the Sheet URL |