npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@figma-vars/hooks

v4.0.0

Published

Typed React hooks for managing Figma Variables, modes, tokens, and bindings via API.

Downloads

541

Readme

FigmaVars/hooks

Built and maintained by Mark Learst.

A fast, typed React 19.2.3 hooks library for the Figma Variables API: fetch, update, and manage design tokens via the official Figma REST API.

Built for the modern web, this library provides a suite of hooks to fetch, manage, and mutate your design tokens/variables, making it easy to sync them between Figma and your React applications, Storybooks, or design system dashboards.

| Package | Quality | Activity | | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | npm version | CI | GitHub last commit | | npm downloads | codecov | Status | | bundle size | Test Coverage | License | | node version | TypeScript | GitHub code size |

📌 Why 4.0.0

  • New Utilities: withRetry() for automatic retry with exponential backoff, redactToken() for safe logging
  • 🔧 Flexible API: baseUrl option for fetcher/mutator, caseInsensitive option for filterVariables
  • 🛡️ Better Error Handling: Improved parsing for non-JSON API responses (HTML, plain text)
  • 🐛 Critical Bug Fix: SWR cache keys now correctly separate fallback and live data
  • 📚 Improved Docs: Comprehensive mutation return type documentation with examples
  • 📦 Modern Tooling: Node 20+ toolchain, strict TypeScript, and ESM-first packaging with CJS interop
  • 🖥️ CLI Export Tool: Automate variable exports with figma-vars-export for CI/CD (Enterprise required)

⚠️ Breaking Change: useFigmaToken is now a named export. See Migration Guide.

🚀 Features at a Glance

  • Modern React 19.2 hooks for variables, collections, modes, and published variables
  • Ergonomic mutation hooks with consistent loading/error states
  • SWR configuration support for customizing caching and revalidation behavior
  • Error handling utilities for type-safe error checking and status code access
  • Cache invalidation helpers for automatic data refresh after mutations
  • CLI export tool (figma-vars-export) for automating variable exports to JSON (Enterprise required)
  • Fallback JSON support (object or string) for offline/static use - works without Enterprise!
  • Typed core entrypoint for non-React consumers (Axios, TanStack Query, server scripts)
  • 100% test coverage + strict TypeScript + clean exports/attw/publint/size-limit checks

📦 Install

npm install @figma-vars/hooks
# or
pnpm add @figma-vars/hooks

Peer deps: react and react-dom.

🖥️ CLI Export Tool

The package includes a CLI tool (figma-vars-export) for automatically exporting Figma variables to JSON via the REST API. Perfect for CI/CD pipelines, build scripts, or one-off exports.

⚠️ Enterprise Required: The CLI tool uses the Figma Variables REST API, which requires a Figma Enterprise account. Without Enterprise, use the Dev Mode plugin export method instead.

# Using npx (no install needed)
FIGMA_TOKEN=your_token npx figma-vars-export --file-key YOUR_FILE_KEY --out ./variables.json

# After installing
npm install @figma-vars/hooks
FIGMA_TOKEN=your_token figma-vars-export --file-key YOUR_FILE_KEY --out ./variables.json

# Show help
figma-vars-export --help

Options:

  • --file-key - Figma file key (required, or set FIGMA_FILE_KEY env var)
  • --out - Output path (default: data/figma-variables.json)
  • --help - Show help message

Environment Variables:

  • FIGMA_TOKEN or FIGMA_PAT - Figma Personal Access Token (required)
  • FIGMA_FILE_KEY - Figma file key (optional)

Example Output:

Saved variables to ./variables.json
Variables count: 42

No Enterprise? See Exporting variables for fallback for alternative methods that work without Enterprise.

🛠️ Quick Start (SWR-powered hooks)

import { FigmaVarsProvider, useVariables } from '@figma-vars/hooks'

const FIGMA_TOKEN = import.meta.env.VITE_FIGMA_TOKEN
const FIGMA_FILE_KEY = 'your-file-key'

function App() {
  return (
    <FigmaVarsProvider
      token={FIGMA_TOKEN}
      fileKey={FIGMA_FILE_KEY}
      swrConfig={{
        revalidateOnFocus: false,
        dedupingInterval: 5000,
      }}>
      <Tokens />
    </FigmaVarsProvider>
  )
}

function Tokens() {
  const { data, isLoading, error } = useVariables()
  if (isLoading) return <div>Loading…</div>
  if (error) return <div>Error: {error.message}</div>
  const variables = Object.values(data?.meta.variables ?? {})
  return <pre>{JSON.stringify(variables, null, 2)}</pre>
}

🧩 Non-SWR Usage (Core entrypoint)

Use the /core build when you prefer Axios/TanStack/server scripts without React/SWR.

Axios example (GET + bulk PUT)

import axios from 'axios'
import { FIGMA_FILE_VARIABLES_PATH } from '@figma-vars/hooks/core'

const token = process.env.FIGMA_TOKEN!
const fileKey = process.env.FIGMA_FILE_KEY!

// Fetch local variables
const url = `https://api.figma.com${FIGMA_FILE_VARIABLES_PATH(fileKey)}/local`
const { data } = await axios.get(url, {
  headers: { 'X-FIGMA-TOKEN': token, 'Content-Type': 'application/json' },
})

// Bulk update
await axios.put(
  `https://api.figma.com${FIGMA_FILE_VARIABLES_PATH(fileKey)}`,
  { variables: [{ action: 'UPDATE', id: 'VariableId:123', name: 'new-name' }] },
  { headers: { 'X-FIGMA-TOKEN': token, 'Content-Type': 'application/json' } }
)

TanStack Query example

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { FIGMA_FILE_VARIABLES_PATH, fetcher, mutator } from '@figma-vars/hooks/core'

const token = process.env.FIGMA_TOKEN!
const fileKey = process.env.FIGMA_FILE_KEY!

export function useLocalVariables() {
  return useQuery({
    queryKey: ['figma-local', fileKey],
    queryFn: () => fetcher(`${FIGMA_FILE_VARIABLES_PATH(fileKey)}/local`, token),
    staleTime: 60_000,
  })
}

export function useBulkUpdate() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (payload: unknown) =>
      mutator(FIGMA_FILE_VARIABLES_PATH(fileKey), token, 'UPDATE', payload),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['figma-local', fileKey] }),
  })
}

🛡️ Fallback JSON (offline/static)

Pass fallbackFile (object or JSON string) to FigmaVarsProvider to bypass live API calls:

import exportedVariables from './figma-variables.json'
;<FigmaVarsProvider
  token={null}
  fileKey={null}
  fallbackFile={exportedVariables}>
  <App />
</FigmaVarsProvider>

Exporting variables for fallback

There are several ways to get your Figma variables as JSON:

  1. Dev Mode / plugin export (recommended, no Enterprise needed)

    • Use a Variables exporter plugin in Figma Dev Mode to download the full Variables panel as JSON
    • Save anywhere (e.g., data/figma-variables.json) and pass it to fallbackFile
    • Works for everyone, no Enterprise account required!
  2. CLI export tool (Enterprise required) 🚀

    • Automatically exports via REST API - perfect for CI/CD and automation
    • See the CLI Export Tool section above for full usage details
    • Also available from cloned repo: node scripts/export-variables.mjs --file-key KEY --out file.json
  • Desktop MCP (manual/partial): Selecting a frame and running get_variable_defs returns only that selection’s variables. Use plugin/REST exports for complete coverage.
  1. Style Dictionary
    • Once you have the JSON (from any path), feed it into Style Dictionary to emit platform-specific artifacts
    • Or import it directly via fallbackFile

🔧 Mutation Hooks (verbs fixed)

  • useCreateVariable → POST via bulk endpoint with action: 'CREATE'
  • useUpdateVariable → PUT via bulk endpoint with action: 'UPDATE'
  • useDeleteVariable → DELETE via bulk endpoint with action: 'DELETE'
  • useBulkUpdateVariables → PUT bulk payload (collections, modes, variables, values)

All return { mutate, data, error, isLoading, isSuccess, isError }.

Example: Creating and updating variables

import { useCreateVariable, useUpdateVariable, useInvalidateVariables } from '@figma-vars/hooks'

function VariableEditor() {
  const { mutate: create } = useCreateVariable()
  const { mutate: update } = useUpdateVariable()
  const { invalidate } = useInvalidateVariables()

  const handleCreate = async () => {
    await create({
      name: 'Primary Color',
      variableCollectionId: 'CollectionId:123',
      resolvedType: 'COLOR',
    })
    invalidate() // Refresh cache after mutation
  }

  const handleUpdate = async (id: string) => {
    await update({
      variableId: id,
      payload: { name: 'Updated Name' },
    })
    invalidate() // Refresh cache after mutation
  }

  return (
    <>
      <button onClick={handleCreate}>Create Variable</button>
      <button onClick={() => handleUpdate('VariableId:123')}>Update</button>
    </>
  )
}

🛡️ Error Handling

Error Boundaries (Recommended)

Wrap your Figma-connected components with an error boundary to gracefully handle errors:

import { ErrorBoundary } from 'react-error-boundary'
import { FigmaVarsProvider } from '@figma-vars/hooks'

function FigmaErrorFallback({ error }: { error: Error }) {
  return (
    <div role='alert'>
      <h2>Failed to load Figma data</h2>
      <pre>{error.message}</pre>
    </div>
  )
}

function App() {
  return (
    <ErrorBoundary FallbackComponent={FigmaErrorFallback}>
      <FigmaVarsProvider
        token={FIGMA_TOKEN}
        fileKey={FIGMA_FILE_KEY}>
        <YourApp />
      </FigmaVarsProvider>
    </ErrorBoundary>
  )
}

Note: The provider validates fallback file structure at runtime and logs warnings in development. Invalid fallback data won't crash the app but will result in undefined data.

Runtime Validation

Use type guards to validate data at runtime:

import { isLocalVariablesResponse, isPublishedVariablesResponse } from '@figma-vars/hooks'

// Validate before using
if (isLocalVariablesResponse(data)) {
  // Safe to access data.meta.variables
}

Error Utilities

v3 introduces powerful error handling utilities for type-safe error checking:

import { isFigmaApiError, getErrorStatus, getErrorMessage, hasErrorStatus } from '@figma-vars/hooks'

function ErrorHandler({ error }: { error: Error | null }) {
  if (!error) return null

  // Type guard for FigmaApiError
  if (isFigmaApiError(error)) {
    const status = error.statusCode

    if (status === 401) {
      return <div>Authentication required. Please check your token.</div>
    }
    if (status === 403) {
      return <div>Access forbidden. Check file permissions.</div>
    }
    if (status === 429) {
      return <div>Rate limit exceeded. Please wait before retrying.</div>
    }
    if (status === 404) {
      return <div>File or variable not found.</div>
    }
  }

  // Helper functions
  const status = getErrorStatus(error) // number | null
  const message = getErrorMessage(error) // string

  // Convenience check
  if (hasErrorStatus(error, 401)) {
    // Handle unauthorized
  }

  return <div>Error: {message}</div>
}

Common HTTP Status Codes:

  • 401 - Unauthorized (invalid or missing token)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found (file/variable doesn't exist)
  • 429 - Too Many Requests (rate limit exceeded)

🔄 Cache Management

After mutations, use useInvalidateVariables to refresh cached data:

import { useUpdateVariable, useInvalidateVariables } from '@figma-vars/hooks'

function UpdateButton({ variableId }: { variableId: string }) {
  const { mutate, isLoading } = useUpdateVariable()
  const { invalidate, revalidate } = useInvalidateVariables()

  const handleUpdate = async () => {
    await mutate({
      variableId,
      payload: { name: 'New Name' },
    })

    // Option 1: Invalidate (refetch on next access)
    invalidate()

    // Option 2: Revalidate immediately (refetch now)
    // revalidate()
  }

  return (
    <button
      onClick={handleUpdate}
      disabled={isLoading}>
      {isLoading ? 'Updating...' : 'Update Variable'}
    </button>
  )
}

⚙️ SWR Configuration

Customize SWR behavior globally through the provider:

<FigmaVarsProvider
  token={FIGMA_TOKEN}
  fileKey={FIGMA_FILE_KEY}
  swrConfig={{
    revalidateOnFocus: false, // Don't refetch on window focus
    dedupingInterval: 5000, // Dedupe requests within 5s
    errorRetryCount: 3, // Retry failed requests 3 times
    errorRetryInterval: 1000, // Wait 1s between retries
    onError: error => {
      // Global error handler
      if (isFigmaApiError(error) && error.statusCode === 429) {
        console.warn('Rate limited, backing off...')
      }
    },
  }}>
  <App />
</FigmaVarsProvider>

Common SWR Options:

  • revalidateOnFocus - Refetch when window regains focus (default: true)
  • dedupingInterval - Deduplication interval in ms (default: 2000)
  • errorRetryCount - Max retry attempts (default: 5)
  • refreshInterval - Polling interval in ms (default: 0 = disabled)
  • onError - Global error callback

📚 API Cheat Sheet

Hooks

  • Queries: useVariables (local), usePublishedVariables (library/published), useVariableCollections, useVariableModes, useFigmaToken
  • Granular Selectors: useCollectionById, useModesByCollection, useVariableById (optimized selectors for specific entities)
  • Mutations: useCreateVariable, useUpdateVariable, useDeleteVariable, useBulkUpdateVariables
  • Cache: useInvalidateVariables (invalidate/revalidate cache)

Utilities

  • Filtering: filterVariables (filter by type, name, with optional caseInsensitive matching)
  • Retry: withRetry (automatic retry with exponential backoff for rate limits)
  • Security: redactToken (safely redact tokens for logging/display)
  • Error Handling: isFigmaApiError, getErrorStatus, getErrorMessage, hasErrorStatus, isRateLimited, getRetryAfter
  • Type Guards: isLocalVariablesResponse, isPublishedVariablesResponse, validateFallbackData (runtime validation)
  • SWR Keys: getVariablesKey, getPublishedVariablesKey, getInvalidationKeys (centralized cache key construction)
  • Core helpers: fetcher, mutator (with baseUrl option), constants for endpoints and headers

Types

  • Responses: LocalVariablesResponse, PublishedVariablesResponse
  • Variables: FigmaVariable, FigmaCollection, VariableMode
  • Mutations: BulkUpdatePayload, CreateVariablePayload, UpdateVariablePayload
  • Errors: FigmaApiError (extends Error with statusCode)

🔐 Auth & Scope

  • Header: X-FIGMA-TOKEN: <PAT>
  • Scopes: file_variables:read for GETs, file_variables:write for mutations.
  • Enterprise Full seat required for live API; fallback JSON works without a token.

⚠️ Enterprise Requirement and Offline Options

  • The Figma Variables REST API requires a Figma Enterprise seat for live requests. Without Enterprise, live calls will fail even with a valid PAT.
  • The library remains useful without Enterprise: supply fallbackFile (object or JSON string) exported from Figma (Dev Mode plugin, CLI, or Figma MCP server output) and all read hooks work offline/for static deployments.
  • MCP/other exporters: as long as they emit the same JSON shape as the Variables API, you can feed that JSON into fallbackFile; mutations still require Enterprise access.

🚫 Do Not Publish Tokens or File Keys

  • Never commit PATs or file keys to git, Storybook static builds, or client bundles.
  • Use environment variables (process.env / import.meta.env) and secret managers; keep them server-side where possible.
  • Prefer fallbackFile with token={null}/fileKey={null} for demos and public Storybooks.
  • Use redactToken() when logging tokens for debugging:
import { redactToken } from '@figma-vars/hooks'

// Safe logging
console.log('Using token:', redactToken(token))
// Output: "Using token: figd_***...***cret"

📈 Rate Limits

  • Figma enforces per-token limits. Rely on SWR/TanStack caching, avoid unnecessary refetches, and prefer fallback JSON for static sites.
  • Use swrConfig to customize dedupingInterval and errorRetryCount to optimize API usage.
  • Use withRetry() utility for automatic retry with exponential backoff on 429 errors:
import { withRetry, fetcher } from '@figma-vars/hooks'

const fetchWithRetry = withRetry(() => fetcher('/v1/files/KEY/variables/local', token), {
  maxRetries: 3,
  onRetry: (attempt, delay) => console.log(`Retry ${attempt}...`),
})

📚 Storybook & Next.js

  • Storybook decorator: wrap stories once so hooks have context and tokens.
// .storybook/preview.tsx
import { FigmaVarsProvider } from '@figma-vars/hooks'
import type { Preview } from '@storybook/react'

const FIGMA_TOKEN = process.env.STORYBOOK_FIGMA_TOKEN
const FIGMA_FILE_KEY = process.env.STORYBOOK_FIGMA_FILE_KEY

const preview: Preview = {
  decorators: [
    Story => (
      <FigmaVarsProvider
        token={FIGMA_TOKEN}
        fileKey={FIGMA_FILE_KEY}>
        <Story />
      </FigmaVarsProvider>
    ),
  ],
}

export default preview
  • Next.js App Router: provide context in a shared provider file.
// app/providers.tsx
import { FigmaVarsProvider } from '@figma-vars/hooks'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <FigmaVarsProvider
      token={process.env.NEXT_PUBLIC_FIGMA_TOKEN}
      fileKey={process.env.NEXT_PUBLIC_FIGMA_FILE_KEY}>
      {children}
    </FigmaVarsProvider>
  )
}

🧪 Tooling & Quality Gates

  • pnpm run build, pnpm test, pnpm run test:coverage
  • pnpm run check:publint, pnpm run check:attw, pnpm run check:size

🧭 Release Checklist (for 4.0.0)

  • Run pnpm run check:release
  • Run pnpm version major (creates v4.0.0 tag)
  • CI publishes to npm automatically
  • Update dist-tags on npm if needed (latest → 4.0.0)

🔄 Migration Guide (3.x → 4.0)

Breaking Change: useFigmaToken Export

// Before (3.x) - NO LONGER WORKS
import useFigmaToken from '@figma-vars/hooks'

// After (4.0) - USE THIS
import { useFigmaToken } from '@figma-vars/hooks'

New Utilities (opt-in)

import { withRetry, redactToken, filterVariables } from '@figma-vars/hooks'

// Automatic retry with exponential backoff
const fetchWithRetry = withRetry(() => myApiCall(), { maxRetries: 3 })

// Safe token logging
console.log('Token:', redactToken(token)) // "figd_***...***cret"

// Case-insensitive filtering
filterVariables(vars, { name: 'primary', caseInsensitive: true })

Custom API Base URL

import { fetcher, mutator } from '@figma-vars/hooks/core'

// Use mock server for testing
await fetcher('/v1/files/KEY/variables/local', token, {
  baseUrl: 'http://localhost:3000',
})

📝 Contributing

PRs and issues are welcome! Please see CONTRIBUTING.md for guidelines.

📝 License

This project is licensed under the MIT License. © 2024–2026 Mark Learst