VarVar is a Figma plugin that allows you to export your Figma variables to JSON, CSV, CSS, or JavaScript formats, and import them back in from JSON, making it easier to integrate your design tokens into your development workflow.
- Multiple Export Formats: Export Figma variables to JSON, CSV, CSS (vanilla or Tailwind CSS v4), or JavaScript
- JSON Import: Re-populate collections, modes, variables, and linked-variable references from a previously exported JSON file, choosing between four reconciliation modes and reviewing a dry-run diff before anything is written;
rem/emvalues are converted back using a root font size you supply, and colour tokens are read in every colour space the DTCG Color Module defines β see below. CSV/CSS/JS import isn't supported, since those formats aren't reliable round-trip sources. - Format-Specific Menu Commands: Direct access to each export format from the Figma menu
- Linked Variable Support: Identifies and properly handles linked variables across formats
- Scope-Aware Types: JSON, CSV, and JS exports map variable scopes (
CORNER_RADIUS,FONT_WEIGHT,OPACITY, etc.) to DTCG$types instead of exporting bare numbers; in JSON the$typealso has to fit the value it sits beside β see below - Unit Options: CSS, Tailwind, and JSON exports choose the unit dimension values carry β none,
px, orremwith a configurable root font size; CSV and JavaScript always emit bare numbers β see below - DTCG-Compliant Values: JSON writes dimensions and colours in the object shapes the Design Tokens spec defines for them by default, with a toggle for the
"16px"and"#ff00ff"strings earlier versions emitted; CSS, Tailwind, JS, and CSV always write colours as CSS strings β see below - Faithful Numbers: every format emits the shortest decimal that round-trips back to the 32-bit float Figma actually stores, so a
732.8exports as732.8rather than732.7999877929688 - Scope-Driven Tailwind Naming (BETA): The Tailwind CSS export takes each variable's theme namespace and unit from its Figma scopes rather than guessing from its name β see below
- Code Syntax Support: Figma's per-variable Code Syntax (Web, Android, iOS) is exported in JSON, CSV, and JS, and applied again on import; CSS and JS can optionally use the Web syntax as the emitted variable name
- Extended Collection Hierarchy Export (Enterprise, BETA): All export formats detect Enterprise extended collections and preserve the inheritance model instead of flattening it β see below
- Preview & Copy: Preview exported data β syntax-highlighted and legible in both Figma's light and dark themes β and easily copy to clipboard
- Automatic Downloads: Exported files are automatically downloaded
- Row/Column Positioning: CSV option for spreadsheet formula-like linking
- JSON: Linked variables start with
$.VARIABLE.PATH - JavaScript: Linked variables are referenced directly like
collection.mode.variable- Numeric paths are converted to bracket notation:
collection.mode["500"]
- Numeric paths are converted to bracket notation:
- CSV: Linked variables start with
=VARIABLE/PATH- Option: Use row & column positions to produce formula-like linking (i.e.
=E7) in spreadsheet programs
- Option: Use row & column positions to produce formula-like linking (i.e.
- CSS: Linked variables use CSS custom property syntax:
--var-name: var(--VARIABLE) - Tailwind CSS: Linked variables use CSS custom property syntax with Tailwind naming conventions (i.e.
var(--color-brand-500)), resolved through the same scope-driven namespace and group separator as the declaration itself
Note: When dealing with linked variables that have multiple modes, the plugin will only link to the first occurrence (i.e., the first mode).
The CSS export can emit a Tailwind CSS v4 @theme block instead of plain custom properties. Naming is driven by each variable's Figma scopes rather than by its name:
- The scopes pick the theme namespace: font size becomes
--text-*, font weight--font-weight-*, font family--font-*, letter spacing--tracking-*, line height--leading-*, corner radius--radius-*, any colour scope--color-*, gap / width & height / stroke and other dimensions--spacing-*, and opacity--opacity-*. Variables left on Figma's default scoping β or whose scopes point at no namespace, or disagree with each other β fall back to the older name-based guesses. - Units follow the scopes too, so a font weight now exports as
600rather than600px. - A leading group that merely repeats the namespace is dropped:
text/h1scoped to font size becomes--text-h1, not--text-text--h1.
Two options shape the output:
- Join groups with a single dash (both CSS outputs; on for Tailwind, off for plain CSS until you set it yourself): Figma's
/group delimiter becomes a single-, socolor/brand/500exports as--color-brand-500. Tailwind IntelliSense only suggests theme variables written that way, so it can complete them in@applyandclass=""; turn the option off for--color-brand--500. Dashes you typed into the Figma name yourself are always preserved, so a variable namedtext/h1--line-heightexports as--text-h1--line-heightβ which is also why the switch is offered for vanilla CSS, where it makes the output paste straight into a Tailwind@themeblock. - Unit for numeric values and Apply the unit to unscoped numeric values: shared with the plain CSS output, and described under Units, Colours and DTCG Values.
If two different Figma variable names produce the same CSS variable name, only one declaration survives; the collisions are listed in a comment at the top of the output.
Tailwind export is flagged BETA in the plugin, and this naming behaviour may still shift β we'd love your feedback: open an issue if a variable lands in the wrong namespace.
Number variables that Figma scopes as dimensions β spacing, corner radius, font size, letter spacing, and so on β are exported with a unit. Two options decide which unit, and which variables get one:
- Unit for numeric values (CSS, Tailwind, and JSON;
pxby default): choosepx,rem, or None (bare number).remreveals a root font size field (16 by default) that the Figma number is divided by, so a32exports as2rem; an empty or invalid entry falls back to 16. Only these units are offered:pxandremare the two a DTCGdimensioncan express, and relative units (em,%,vw, β¦) depend on a context the export can't see. CSV and JavaScript are unaffected β a CSV cell and a JS value have always been bare numbers, and still are. - Apply the unit to unscoped numeric values (CSS, Tailwind, and JSON; off by default): number variables left on Figma's default scoping (no scopes, or all of them) export as bare numbers. Turn this on and they're treated as dimensions too, taking whichever unit is selected above. Variables explicitly scoped to something that isn't a dimension β font weight, opacity β stay unitless either way.
For JSON there is one more:
- DTCG-compliant values (JSON only, on by default): the Design Tokens spec requires an object where a string won't do. A dimension is written as
{ "value": 16, "unit": "px" }, and a colour as the Color Module's object β"colorSpace": "srgb", the three channels as 0β1 numbers, analphaonly when it isn't 1 (the spec says an omitted alpha means fully opaque), and a 6-digithexfallback alongside. Turn it off to get the"16px"and"#ff00ff"strings earlier versions of the plugin emitted, for tooling built against those. Numbers with no unit are bare numbers either way. This is JSON only β CSS, Tailwind, JavaScript, and CSV always write colours as CSS strings.
A token's $type in JSON is a claim about the $value beside it, so it's derived from what's actually being emitted rather than from the variable's scopes alone:
- A number exported with a unit is a
dimension; the same variable with the unit set to none is anumber. - A scope that contradicts the variable's own data type β a string scoped to font size, a number scoped to font family β doesn't decide the type; the value does.
fontWeightconstrains the value itself, so a variable stays afontWeightonly when it holds a number from 1 to 1000 or one of the spec's predefined keywords, spelled exactly. A1200, or a"Bold"the case-sensitive keyword table doesn't list, is emitted as anumberor astringinstead.
The value is never clamped, rounded, or rewritten to make it fit a type β only the type gives way. CSV's DTCG Type column and JavaScript's dtcgType field are unaffected: they describe what a variable is scoped to, rather than claiming conformance for a value, and are unchanged.
The unit and DTCG options don't apply to the legacy (v2.x) JSON shape, which predates them and always emits bare numbers and CSS colour strings; they're hidden while that toggle is on.
Across every format, numbers are emitted as the shortest decimal that round-trips back to the 32-bit float Figma stores. A 732.8 typed in Figma exports as 732.8 instead of 732.7999877929688, and values that genuinely need more digits keep them.
Figma lets you give each variable a per-platform Code Syntax (Web, Android, iOS). VarVar carries it through:
- JSON: under
$extensions.figma.codeSyntax - CSV: three trailing columns β
Code Syntax (Web),Code Syntax (Android),Code Syntax (iOS) - JavaScript: a
codeSyntaxfield on each value - Import: any code syntax in the JSON is applied to the variables that are created or updated, and shown in the import preview. A file that says nothing about code syntax never clears an existing one.
CSS and JavaScript exports also offer Use Web code syntax as variable name: variables that have a Web code syntax set are emitted under that name, both where they're declared and in every reference to them. In CSS a leading -- is kept rather than doubled, and overrides that can't be a custom property name are ignored and listed in a comment. In JavaScript the name is camelCased if it isn't already a valid identifier, and if it still can't be one the derived name is used.
Figma's Enterprise-only extended collections let a "child" collection (e.g. a brand) inherit from a "parent" (e.g. a base design system) and override only what differs. All four export formats now detect this and preserve the hierarchy instead of flattening it:
- Values actually overridden in the child collection keep their own value.
- Everything else is exported as a reference into the parent collection's tokens (a
$valuealias in JSON, a plain CSS cascade in CSS, an=Collection/mode/Variablereference in CSV, a property-path reference in JS). - Every value is tagged as inherited or overridden (
$extensions.figma.inheritedin JSON, anInheritedcolumn in CSV, aninheritedfield in JS; CSS relies on the cascade itself rather than a tag). - JSON only: output splits into
base.tokens.json(all non-extended collections) plus one file per extended collection, bundled into a single.zipdownload. CSS, CSV, and JS stay single-file, representing inheritance inline.
This only activates when extended collections are present in the file β accounts without Enterprise extended collections see no change to their exports. This feature is new and we haven't been able to validate it against a real Enterprise file ourselves, so we'd love your feedback: open an issue if the output doesn't look right.
- Open Figma and go to the Community tab
- Search for "VarVar"
- Click on the plugin and then click "Install"
Access format-specific exports directly from the Figma menu:
- Open your Figma file containing variables
- Go to Plugins β VarVar β Choose your format:
- Export as JSON - Structured JSON data with nested groups
- Unit for numeric values -
px(default),remwith a root font size, or none - Apply the unit to unscoped numeric values - opt-in unit for numbers left on Figma's default scoping
- DTCG-compliant values - on by default; off restores the
"16px"and"#ff00ff"strings
- Unit for numeric values -
- Export as JavaScript - JavaScript objects with proper references (always bare numbers)
- Export as CSV - Spreadsheet-compatible data (always bare numbers)
- Export as CSS & Tailwind - CSS custom properties for web development
- Tailwind CSS or vanilla - Tailwind CSS format with
@themedirective (BETA) - Join groups with a single dash - available for both CSS outputs
- Unit for numeric values and Apply the unit to unscoped numeric values - as for JSON
- Use Web code syntax as variable name - also available for JavaScript
- Tailwind CSS or vanilla - Tailwind CSS format with
- Export as JSON - Structured JSON data with nested groups
- Configure filename and options (if applicable)
- Click "Export Variables"
- The exported file will be automatically downloaded
For format selection within the interface:
- Open your Figma file containing variables
- Go to Plugins β VarVar β Export Variables
- Choose your desired export format
- Configure filename and options
- Click "Export Variables"
- The exported file will be automatically downloaded
- Toggle the "Preview output" switch to see the exported data within the plugin interface
- Use the "Select to Copy" button and copy (Ctrl/Cmd + C) the exported data to your clipboard
Note: Programmatically copying is currently not supported by Figma Plugin APIs.
- Open your Figma file
- Go to Plugins β VarVar β Importβ¦
- Choose one or more JSON files previously exported by VarVar (current or legacy format; if you exported an Enterprise extended-collection
.zip, select its unzipped files together) - Choose how the file should be reconciled with the variables already in the document:
- Merge (default) β creates missing collections, modes, and variables and updates the ones that match by name. Nothing is ever deleted, so existing component links are never broken.
- Update only β touches only what exists in both the file and the document. Nothing is created and nothing is deleted.
- Merge and delete anything not in the file β merges, then deletes any variable, mode, or whole collection the file doesn't mention. Matches are updated in place, so their component links survive.
- Clean import β deletes every existing local variable collection first, not just the ones named in the JSON, then imports fresh. Every component link is broken, even for variables that match the file exactly.
- Click Preview import for a dry-run diff of exactly what would be created, updated, deleted, or left unchanged, down to per-mode values and code syntax, without writing anything
- If the file carries
remoremvalues, which Figma has no equivalent for, a root font size field appears. Every such value is multiplied by it β with 16,2rembecomes32β and the diff re-runs as you change it, so what you see is what would be written - Click Confirm import to apply it (the two deleting modes ask you to confirm first). Collections, modes, variables, and linked-variable references are recreated, and a summary of what was created/updated (plus any warnings) is shown
Note: Import reads dimensions in both spellings β the DTCG
{ "value": 16, "unit": "px" }object and the older"16px"string β even mixed within one file. A unit that can't be reversed into a Figma number (pt,%,vh, β¦) has its suffix stripped and the number imported as-is, with a warning naming the value.
Note: Colours are read in both spellings too: the DTCG object form, in all fourteen colour spaces the Color Module defines, and the CSS colour strings VarVar has always exported.
srgb,srgb-linear,hsl, andhwbare sRGB in different coordinates and convert exactly. The wide-gamut and perceptual spaces βdisplay-p3,oklch,lab,rec2020, and the rest β are converted to sRGB and named in a warning, because a Figma variable is sRGB and can't hold them faithfully. Where such a colour falls outside the sRGB gamut, the token's ownhexfallback is used if it has one; otherwise it's mapped to the nearest sRGB colour.
Note: A file that doesn't carry VarVar's own
$extensions.figma.resolvedTypeβ a hand-authored or third-party token file β has its Figma variable type derived from the value rather than from$typealone, since a conformantfontWeightmay hold either a number or a keyword. A number creates a FLOAT variable; a keyword creates a STRING holding it verbatim, never translated to a number. If a token's modes disagree, the variable becomes a STRING, which holds every spelling exactly, and the summary warns.
Note: Only JSON is supported for import β CSV, CSS, and JS aren't reliable round-trip sources for reconstructing variables.
Note: A leading
.or_is Figma's own convention for marking a collection, variable, or group "private" (hidden from publishing) β common in real design systems, not an edge case. Import handles it correctly: linked-variable references are matched against your file's actual collection/mode names rather than blindly split on., and any newly created collection/variable whose name starts with.or_getshiddenFromPublishingset to match, so the privacy actually carries over. Check the import summary's warnings for anything it couldn't confidently match.
VarVar is built with a modular architecture for maintainability and scalability:
- Type System: Strict TypeScript enums and interfaces for type safety
- UI Components: Reusable React components for consistent interface
- Format Views: Dedicated views for each export format
- Export Utilities: Format-specific processing functions with JSDoc documentation
src/
βββ components/ # Reusable UI components
β βββ PluginDialogShell.tsx # Layout shell with padding and footer
β βββ ExportHeader.tsx
β βββ ExportLayout.tsx
β βββ FilenameInput.tsx
β βββ ExportButton.tsx
β βββ OutputPreview.tsx
β βββ ExportOptions.tsx
β βββ FileImportInput.tsx # File picker for JSON import
β βββ ImportOptions.tsx # Import reconciliation mode picker
β βββ ConfirmReplaceDialog.tsx # Confirmation for the two deleting import modes
β βββ ImportDiffPreview.tsx # Dry-run diff shown before an import is applied
β βββ ImportSummaryPanel.tsx # Import result counts and warnings
β βββ HelpTip.tsx # "?" badge with a figma-kit tooltip
β βββ Footer.tsx
βββ hooks/ # Custom React hooks
β βββ useExportData.ts # Hook for managing export data and state
β βββ useImportData.ts # Hook for managing import data and state
βββ views/ # Format-specific export/import views
β βββ ExportView.tsx # Generic export with format selector
β βββ ExportJSON.tsx
β βββ ExportCSV.tsx
β βββ ExportCSS.tsx
β βββ ExportJS.tsx
β βββ ImportJSON.tsx # JSON import view
βββ utils/ # Export/import processing utilities
β βββ collectionToJSON.ts
β βββ collectionToCSV.ts
β βββ collectionToCSS.ts
β βββ collectionToJS.ts
β βββ collectionToTailwind.ts
β βββ importJSON.ts # Parses exported JSON and recreates variables in Figma
β βββ clipboard.ts
β βββ color.ts
β βββ colorSpaces.ts # DTCG colour-space maths for reading object-form colours
β βββ numberFormat.ts # Shortest decimals that round-trip through Figma's float32
β βββ units.ts # Export unit choice, rem conversion, unit parsing on import
β βββ stringTransformation.ts
βββ types.d.ts # TypeScript definitions and enums
βββ code.ts # Plugin main logic
βββ ui.tsx # UI router and main app
To set up the development environment:
- Clone the repository
- Install dependencies:
npm install - Run the development server:
npm run dev
To build the plugin for production:
npm run build
VarVar is developed and maintained by Atropical AS.