Developed by sandunf.com
Figma Plugin · Content Management Tool
PixelFlow Logo

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.

Google Sheet Sheet
Figma Figma

Pull copy from your Google Sheet and push it directly into matching Figma text layers. Supports English (EN) and Localized columns.

Figma Figma
Google Sheet Sheet

Read text from Figma layers and write it back to the Suggested Copy (EN) column. Perfect for content audits.

Step 01
Create Sheet
Set up a Google Sheet using the sample structure, then deploy an Apps Script Web App.
Step 02
Connect URL
Paste the Web App URL into the plugin. It auto-fetches all tabs and keys.
Step 03
Sync
Select keys, choose EN or Localized, and hit Sync. Your designs update instantly.

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. 1

    Open the plugin in Figma. On the Setup screen, expand Step 1 - Create your Google Sheet.

  2. 2

    Click ⬇ Download Structure File. A file named PixelFlow_ContentSync_Sample.xlsx downloads.

  3. 3

    Go to Google Drive+ New → File Upload. Select the downloaded file.

  4. 4

    Double-click once uploaded → choose Open with Google Sheets.

Both tabs (Global and Journey Tab) will have section rows coloured - light-green for Global, light-blue for Journey tabs.

Image: Plugin Step 1 - Download Structure File button

Fig.Plugin Setup screen - Download Structure File button

1.2  Sheet Structure

Every tab uses the same 4-column layout.

ColumnPurposeExample
Col ADescription - what the element isCancel Modal Title
Col BUnique String Key - must match your Figma layer name exactlyglb_modal_cancel_title
Col CSuggested Copy (EN) - the English content valueAre you sure you want to cancel?
Col DLocalized - translated version for Localized sync modeWeet u zeker dat u wilt annuleren?
Global
Journey Tab
Col A - DescriptionCol B - KeyCol C - EN CopyCol D - Localized
Uncategorize
Back Buttonglb_btn_backBack
Cancel Buttonglb_btn_modal_cancelCancel
Cancel Modal
Cancel Modal Titleglb_modal_cancel_titleAre you sure?Weet u zeker?
Global tab holds shared components with light-green section rows. Journey Tabs hold page-specific copy with light-blue section rows. Duplicate and rename Journey Tab for additional pages.

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.

Google Sheet - Col B
lp_hero_first_name
Figma Layer Name
lp_hero_first_name
lp_hero_first_name, LP_HERO_FIRST_NAME, and lp hero first name all match - the plugin normalises before comparing.
Tip: Use the Annotate toggle to overlay key labels on your canvas so you can instantly see which layers are connected.

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. 1

    In your Google Sheet, click Extensions → Apps Script. A new tab opens.

  2. 2

    Select all existing code (Ctrl+A / Cmd+A) and delete it.

2.2  Copy and Paste the Script

  1. 3

    Back in PixelFlow, expand Step 2 - Add the Apps Script.

  2. 4

    Click Copy on the code block. The full script is now on your clipboard.

  3. 5

    Paste into the Apps Script editor and press 💾 Save (Ctrl+S / Cmd+S).

Apps Script - PixelFlow Content Sync v4
// 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 };
}
Never modify the Apps Script code unless you understand the expected response format. The plugin will break silently if the structure changes.

2.3  Deploy as a Web App

  1. 6

    Click Deploy → New deployment in the Apps Script editor.

  2. 7

    Click the gear ⚙ → select type → Web app.

  3. 8

    Set Execute as: Me and Who has access: Anyone. Click Deploy.

  4. 9

    Authorise when prompted. Copy the Web App URL - you'll paste it in Step 3.

Your Web App URL format
https://script.google.com/macros/s/ABC123xyz…/exec
Every time you edit the script, you must create a New version: Deploy → Manage deployments → Edit ✏ → New version → Deploy. Otherwise the old script keeps running.
🚀

Image: Apps Script - Deploy as Web App dialog (Execute as: Me, Who has access: Anyone)

Fig.Deployment settings - anyone must have access for the plugin to connect

Setup · Step 3

Connect the Plugin

With your Web App URL ready, connecting takes under 10 seconds.

  1. 1

    In PixelFlow, expand Step 3 - Connect your Web App URL.

  2. 2

    Paste the Web App URL from Step 2 into the input field.

  3. 3

    Click Connect & Sync. The plugin fetches all tabs and switches to the Sync view.

A green dot and your Sheet URL appear in the plugin header. All your sheet tabs load as navigation tabs in the Sync view.

Connection Error Reference

ErrorCauseFix
HTML page returnedWho has access set to Only myselfRe-deploy with Anyone
Network errorURL incorrect or script not deployedVerify URL, re-deploy
Unexpected formatScript code is outdatedRe-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.

AreaWhat it does
Mode toggleSwitch between Sheet → Figma (blue) and Figma → Sheet (purple)
Tab barOne tab per sheet tab - click to switch between Global, Journey Tab, etc.
Search barFilter keys by Unique String Key or Description in real time
EN / LocalizedChoose which column to sync: Col C (EN) or Col D (Localized)
Annotate toggleOverlay key labels on your Figma canvas above matching text layers
Page barTab name, selected count, ↻ Fetch button, Collapse/Expand all
Key listExpandable sections. Check keys to include in the next sync
Sync barStatus 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. 1

    Ensure Sheet → Figma mode is selected (blue).

  2. 2

    Select the tab (e.g. Global or Journey Tab).

  3. 3

    Choose language: EN (Col C) or Localized (Col D).

  4. 4

    Check the keys you want to sync. Use Select all to grab everything.

  5. 5

    Click ⬇ Sync to Figma. Sheet data auto-refreshes before syncing.

The plugin auto-refreshes Sheet data every time you click Sync - you always get the latest copy even if the Sheet was edited after you connected.

Match Indicators

✓ Match

A Figma text layer with this exact key name was found on the current page.

! Not found

No matching Figma layer found. Check the layer is named correctly and on the current page.

Loc

This key has a localized value available in Column D.

🎬

GIF: Sheet → Figma sync in action - keys being pushed into Figma layers live

Fig.Bi-directional sync: Sheet content populating Figma text layers in real time

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. 1

    Click Figma → Sheet in the mode toggle (turns purple).

  2. 2

    Select the keys you want to push back via checkboxes.

  3. 3

    Click ⬆ Push to Sheet.

  4. 4

    A confirmation dialog shows how many rows will be overwritten. Click Yes, Overwrite.

Warning: This writes to Col C (Suggested Copy EN) and cannot be undone from the plugin. Ensure your Figma content is correct before proceeding.
Figma → Sheet is disabled when Localized mode is selected. Switch to EN mode first to push content back.

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.

tabs
Refreshes all tabs - picks up newly added tabs
keys
Reloads all keys within existing tabs
match
Updates match indicators against the current Figma page
loader
Shows a progress overlay with phase messages during reload
Use Fetch whenever you add new rows or tabs to your Sheet. You do not need to disconnect and reconnect - Fetch handles it live.

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

Annotations are non-destructive. Delete the PixelFlow Annotations frame from the Figma layers panel at any time - it won't affect your content layers.
🏷️

Image: Mapping Layers UI - Canvas with key label annotations above each matched text layer

Fig.Annotate mode - key labels overlaid on Figma canvas for visual layer mapping

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.

FieldColumnNotes
DescriptionCol AHuman-readable label for the component
Unique String KeyCol BOne-click Copy button - paste directly into a Figma layer name
Suggested Copy (EN)Col CCurrent English content value
LocalizedCol DTranslated 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

ItemDetailNotes
Global tabLight-green section rowsShared components: buttons, modals, nav
Journey TabLight-blue section rowsPage-specific copy: hero, features, CTA
Col ADescriptionHuman-readable label
Col BUnique String KeyMust match Figma layer name exactly
Col CSuggested Copy (EN)Synced in EN mode
Col DLocalizedSynced in Localized mode

Plugin Controls

ControlWhat it does
Mode toggleBlue = Sheet → Figma  |  Purple = Figma → Sheet
EN / LocalizedSelects which column is used during sync (Col C or Col D)
Annotate toggleShows/hides key labels on the Figma canvas
↻ FetchReloads all keys from the Sheet without syncing
Select all ☑Checks/unchecks all visible keys in the current tab
Collapse / Expand allToggles all section accordions open or closed
ⓘ Info iconOpens a modal with all four column values for that key
⬇ Sync to FigmaPushes Sheet content into matching Figma text layers
⬆ Push to SheetReads Figma layer text and writes it to Col C
⚙ Settings iconReturns to Setup screen to change the Sheet URL
Developed by Sandun Fernando  ·  PixelFlow – Content Sync v4.0