A Figma plugin that extracts design variables (colors, typography, spacing, etc.) and commits them to GitHub as either DTCG-compliant design tokens or a Figma-native JSON structure that mirrors the Variables UI.
- DTCG-Compliant Export: Exports design tokens following the Design Tokens Community Group (DTCG) specification v2025.10
- Figma-Native Export: Generate JSON that preserves collections, groups, modes, and IDs exactly as shown in the Variables table (per collection or aggregated)
- Flexible Output: Export a single JSON or automatically split one file per collection with deterministic file naming
- One-Click Export: Extract all Figma variables with full metadata
- Tab-Based Interface: Organized UI with Export, Preview, and Settings tabs
- Direct GitHub Integration: Commit directly to any branch (creates branch if needed)
- Flexible Branch Diffing: Compare the export against the target branch first, then fall back to a configurable default branch to skip redundant commits
- Smart Change Detection: SHA-256 content hashing prevents redundant commits
- Diff Preview: See token-level changes before committing (added/modified/removed)
- JSON Preview: View and copy any exported JSON document (DTCG or Figma-native) directly in the plugin
- Dry Run Mode: Test without committing to verify changes
- Automatic Conflict Resolution: Auto-retry on 409 conflicts
- Remote Variable Support: Handles references to external/library variables
- Rich Metadata: Includes export timestamp, plugin version, file info, and content hash
- Secure Token Storage: GitHub PAT stored only in Figma's clientStorage (never exported)
- Dark Mode Support: Automatic theme matching with Figma
Note
For the latest release, visit the Releases page.
- Download the latest release from the Releases page.
- Extract the ZIP file to a folder on your computer (remember where you put it!).
- Open Figma Desktop (download from https://www.figma.com/downloads/ if you don't have it).
- In Figma, go to Menu → Plugins → Development → Import plugin from manifest...
- Navigate to the folder where you extracted the files.
- Select
manifest.json. - Done! The plugin will appear under Plugins → Development → FigGit.
Tip
The plugin will remember your settings between sessions. Use the "Settings" tab to configure your repository and save your GitHub token for easy reuse.
Each release ships as a folder containing manifest.json plus a compiled dist/ directory. Keep the structure intact when importing the plugin:
manifest.json– References the compiled assets insidedist/dist/plugin.js– Bundled plugin code that runs in the Figma sandboxdist/index.html(and related assets) – Bundled UI served inside the plugin iframe
If you build from source (npm run build), the same files are generated locally before packaging.
When a new version is available:
- Download the latest build
- Extract it to the same folder (overwrite existing files)
- In Figma Desktop: Plugins → Development → Hot reload plugin
FigGit can export either DTCG (Design Tokens Community Group) format v2025.10 or a Figma-native JSON structure that mirrors the Variables table. Choose the format and document strategy (single file vs per-collection) from Settings → Export Options.
Tokens are organized in nested groups with the following properties:
$value: The token's value in the primary mode$type: Token type (color, dimension, number, string, boolean, fontFamily, etc.)$description: Optional human-readable description$extensions: Tool-specific metadata (Figma stores modes, scopes, codeSyntax here)
Token Path Convention:
- If variable name contains dots: use name as-is (e.g.,
color.bg.default→color.bg.default) - If variable name has no dots: combine collection + variable name (e.g., Collection:
Colors, Variable:primary→colors.primary) - Remote/library variables: prefixed with
external.*
Example DTCG export:
{
"color": {
"bg": {
"default": {
"$value": "#ffffff",
"$type": "color",
"$extensions": {
"com.figma": {
"modes": {
"Light": "#ffffff",
"Dark": "#1a1a1a"
},
"scopes": ["ALL_SCOPES"],
"codeSyntax": {},
"hiddenFromPublishing": false
}
}
}
}
},
"spacing": {
"sm": {
"$value": {
"value": 8,
"unit": "px"
},
"$type": "dimension"
}
},
"$extensions": {
"com.figma": {
"exportedAt": "2025-01-15T10:30:00.000Z",
"fileName": "Design System",
"pluginVersion": "0.2.0",
"collectionsCount": 3,
"variablesCount": 42,
"contentHash": "sha256:a1b2c3..."
}
}
}- Nested Groups: Tokens organized by dot-separated paths (e.g.,
color.bg.default) - Multi-Mode Support: All mode values stored in
$extensions.com.figma.modes(primary mode in$value) - Alias References: References use DTCG format
{path.to.token} - Type Safety: Explicit
$typefor each token with automatic type inference - Smart Type Detection: Dimension vs number distinction based on token path and scopes
- Color Format: Colors exported as hex strings in sRGB color space
- Metadata: Export metadata in
$extensions.com.figmaat root level
| DTCG Type | Figma Type | Example Value |
|---|---|---|
color |
COLOR | "#ffffff" or {color.primary} |
dimension |
FLOAT (with px/rem context) | {"value": 16, "unit": "px"} |
number |
FLOAT | 1.5 |
string |
STRING | "Roboto" |
boolean |
BOOLEAN | true |
fontFamily |
STRING (font context) | "Inter" |
The Figma-native export mirrors collections, groups, and variables exactly as they appear in the Variables table. Each JSON document includes deterministic metadata so change detection and Git diffs continue to work even when splitting into multiple files.
- Top-level metadata:
collectionsCount,variablesCount,contentHash,exportedAt,fileName,pluginVersion,exportFormat,exportType - Per-collection docs: include
collectionId,collectionName, andcollectionVariablesCount - Groups: Real Variable groups (e.g.,
Brand / Surfacing / Primary) preserved as nestedgroups - Variables: Carry Figma IDs, names (leaf node), full
path, scopes, code syntax, and per-modevalueByMode - Modes: Stored once per collection and referenced via
modeId
Example Figma-native export (per collection):
{
"collectionsCount": 1,
"variablesCount": 12,
"contentHash": "c19f6a...",
"exportedAt": "2025-12-03T23:21:11.219Z",
"fileName": "Katalyst Design System",
"pluginVersion": "0.2.0",
"exportFormat": "figma-native",
"exportType": "perCollection",
"collectionId": "889:34",
"collectionName": "Core Colors",
"collections": [
{
"id": "889:34",
"name": "Core Colors",
"modes": [
{ "id": "889:56", "name": "Light" },
{ "id": "889:57", "name": "Dark" }
],
"groups": [
{
"id": "889:34:brand",
"name": "Brand",
"path": "Core Colors/Brand",
"groups": [],
"variables": [
{
"id": "var-brand-primary",
"name": "Primary",
"path": "Brand/Primary",
"type": "color",
"valueByMode": {
"889:56": { "value": "#0F62FE" },
"889:57": { "value": "#78A9FF" }
}
}
]
}
],
"variables": [
{
"id": "var-brand-primary",
"name": "Primary",
"path": "Brand/Primary",
"type": "color",
"valueByMode": {
"889:56": { "value": "#0F62FE" },
"889:57": { "value": "#78A9FF" }
}
}
]
}
]
}Use single-file export when you want one consolidated JSON, or switch to per-collection to create deterministic files such as tokens/core-colors.json, tokens/spacing.json, etc.
- Figma Desktop (plugin API not available in browser version)
- Node.js 20+ (for building the plugin)
- GitHub Repository with write access
- GitHub Personal Access Token (fine-grained or classic with
reposcope)
# Clone and install dependencies
npm install
# Build the plugin
npm run buildIn Figma Desktop:
- Go to Plugins > Development > Import plugin from manifest...
- Select
manifest.jsonfrom this project directory - The plugin will appear in your Plugins menu
Create a Personal Access Token (PAT) with repository write access:
Fine-grained token (recommended):
- Permissions: Contents → Read and write
Classic token:
- Private repos:
reposcope - Public repos:
public_reposcope
The token is stored securely in Figma's clientStorage and never exported or sent back to the UI after saving.
In Figma Desktop, run the plugin from the Plugins menu.
Configure your GitHub repository and authentication:
- Enter GitHub owner (username or organization)
- Enter repository name
- Specify the target branch for token exports (will be created if it doesn't exist)
- (Optional) Provide the repository's default branch used for diff fallback and branch creation
- (Optional) Specify folder path within the repo
- Set output filename (must end with
.json)
- Paste your GitHub Personal Access Token
- Click "Save GitHub Token"
- Click "Validate" to verify access
- Add commit message prefix (e.g.,
feat(tokens):) - Enable dry run mode to test without committing
Extract and commit your design tokens:
- Click "Export Variables" to extract all variables from the current Figma file
- View export status and metadata (variable count, collection count)
- Click "Commit to GitHub" (or "Dry Run" if enabled)
- If variables haven't changed, commit is automatically skipped
- If committed, you'll see a link to the GitHub commit that contains the updated file(s)
Review your export before committing:
- Browse each exported JSON document (expandable, with copy to clipboard)
- Open "Diff Viewer" to see token-level changes (added/removed/changed)
- Review changes before committing
Commit messages are automatically generated with the following format:
[<prefix>] update Figma variables (<count> vars, <collections> collections) - <timestamp>
Example with prefix:
feat(tokens): update Figma variables (128 vars, 7 collections) - 2025-01-15T10:30:00.123Z
Example without prefix:
update Figma variables (42 vars, 3 collections) - 2025-01-15T10:30:00.123Z
The plugin uses SHA-256 content hashing plus targeted branch diffing to detect changes:
- Target Branch First: Compares the export against the current target branch using the embedded
contentHashfield in each JSON payload - Default Branch Fallback: If the target branch does not exist yet, FigGit checks the user-configured default branch (or the target branch when unset) before creating any commits
- Hash Validation: If remote files lack a
contentHash, the plugin treats them as stale so the new export overwrites them with deterministic metadata - Skip Logic: When no differences are detected on the inspected branch, the commit is skipped automatically
Enable dry run to:
- Test configuration without committing
- Preview what would be committed
- Verify diff calculation
- Preserve last known hash state
If the specified branch doesn't exist, the plugin:
- Fetches the configured default branch (or the repository's default if none is specified)
- Creates the new branch from that base branch's HEAD
- Commits the variables file to the new branch
If a 409 conflict occurs (file modified by another process):
- Automatically refetches the latest file SHA
- Retries the commit once
- If second attempt fails, surfaces error to user
src/
├── plugin.ts # Main plugin logic (Figma sandbox)
├── messaging.ts # Type-safe UI ↔ Plugin communication
├── export/
│ ├── buildDtcgJson.ts # DTCG-compliant variable extraction
│ └── hash.ts # Pure JavaScript SHA-256 implementation
├── github/
│ └── githubClient.ts # GitHub API client with base64 encoding
├── shared/
│ └── dtcg-types.ts # DTCG type definitions
├── ui/
│ ├── index.tsx # Preact UI entry point
│ ├── components/ # UI components
│ │ ├── export/ # Export tab components
│ │ ├── preview/ # Preview tab components
│ │ ├── settings/ # Settings tab components
│ │ └── layout/ # Tab layout components
│ ├── context/ # Preact context providers
│ └── hooks/ # Custom Preact hooks
└── util/
├── colorUtils.ts # DTCG color conversion utilities
├── dtcgUtils.ts # DTCG format utilities
├── stableStringify.ts # Deterministic JSON serialization
├── validation.ts # Input validation helpers
└── retry.ts # Retry logic for API calls
# Development mode (watch for changes - both UI and plugin)
npm run dev
# Watch UI only
npm run dev:ui
# Watch plugin only
npm run dev:plugin
# Production build
npm run build
# Create release package (ZIP file)
npm run package
# Run tests
npm test
# Run tests with UI
npm test:ui
# Run tests with coverage
npm test:coverage
# Type checking
npm run type-check
# Linting
npm run lint
npm run lint:fix
# Formatting
npm run format
npm run format:checkFor issues or questions, please open an issue on GitHub.
Note
Developer documentation is coming soon (including contribution guidelines).
All contributions welcome! To contribute to the project, please follow the usual steps:
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly in Figma Desktop
- Submit a pull request
- Language: TypeScript
5.4 - UI Framework: Preact with @create-figma-plugin/ui components
- Build Tools: Vite (UI), esbuild (plugin)
- Testing: Vitest with jsdom
- Code Quality: ESLint, Prettier, Husky, lint-staged
MIT License - see LICENSE file for details
