@figma-vars/hooks 3.0.0-beta.1 โ†’ 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +371 -177
  2. package/dist/api/fetcher.d.ts.map +1 -1
  3. package/dist/api/index.d.ts.map +1 -1
  4. package/dist/api/mutator.d.ts +10 -9
  5. package/dist/api/mutator.d.ts.map +1 -1
  6. package/dist/constants/index.d.ts +2 -28
  7. package/dist/constants/index.d.ts.map +1 -1
  8. package/dist/contexts/FigmaTokenContext.d.ts.map +1 -1
  9. package/dist/contexts/FigmaVarsProvider.d.ts +1 -1
  10. package/dist/contexts/FigmaVarsProvider.d.ts.map +1 -1
  11. package/dist/contexts/index.d.ts +1 -1
  12. package/dist/contexts/index.d.ts.map +1 -1
  13. package/dist/contexts/useFigmaTokenContext.d.ts.map +1 -1
  14. package/dist/core/index.d.cts +8 -0
  15. package/dist/core/index.d.ts +8 -0
  16. package/dist/core/index.d.ts.map +1 -0
  17. package/dist/core.cjs +1 -0
  18. package/dist/core.d.cts +2 -0
  19. package/dist/core.d.ts +2 -0
  20. package/dist/core.mjs +20 -0
  21. package/dist/hooks/index.d.ts +19 -0
  22. package/dist/hooks/index.d.ts.map +1 -1
  23. package/dist/hooks/useBulkUpdateVariables.d.ts.map +1 -1
  24. package/dist/hooks/useCreateVariable.d.ts.map +1 -1
  25. package/dist/hooks/useDeleteVariable.d.ts.map +1 -1
  26. package/dist/hooks/useFigmaToken.d.ts.map +1 -1
  27. package/dist/hooks/useInvalidateVariables.d.ts +31 -0
  28. package/dist/hooks/useInvalidateVariables.d.ts.map +1 -0
  29. package/dist/hooks/usePublishedVariables.d.ts +42 -0
  30. package/dist/hooks/usePublishedVariables.d.ts.map +1 -0
  31. package/dist/hooks/useUpdateVariable.d.ts.map +1 -1
  32. package/dist/hooks/useVariableCollections.d.ts.map +1 -1
  33. package/dist/hooks/useVariableModes.d.ts.map +1 -1
  34. package/dist/hooks/useVariables.d.ts +3 -1
  35. package/dist/hooks/useVariables.d.ts.map +1 -1
  36. package/dist/index-BIUpDTdr.cjs +1 -0
  37. package/dist/index-Cd4HQQHO.js +94 -0
  38. package/dist/index.cjs +1 -1
  39. package/dist/index.d.cts +9 -4
  40. package/dist/index.d.ts +9 -4
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.mjs +235 -162
  43. package/dist/types/contexts.d.ts +29 -3
  44. package/dist/types/contexts.d.ts.map +1 -1
  45. package/dist/types/figma.d.ts +56 -3
  46. package/dist/types/figma.d.ts.map +1 -1
  47. package/dist/types/hooks.d.ts.map +1 -1
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist/types/mutations.d.ts +4 -4
  50. package/dist/types/mutations.d.ts.map +1 -1
  51. package/dist/utils/errorHelpers.d.ts +96 -0
  52. package/dist/utils/errorHelpers.d.ts.map +1 -0
  53. package/dist/utils/filterVariables.d.ts.map +1 -1
  54. package/dist/utils/index.d.ts +2 -1
  55. package/dist/utils/index.d.ts.map +1 -1
  56. package/package.json +75 -31
  57. package/scripts/export-variables.mjs +101 -0
  58. package/dist/index.d.mts +0 -2
package/README.md CHANGED
@@ -1,278 +1,472 @@
1
+ # FigmaVars/hooks
2
+
1
3
  <p align="left">
2
4
  <img src="assets/figma-vars-tagline-light.png" alt="FigmaVars Logo" width="700px" />
3
5
  </p>
4
6
 
5
- A fast, typed React 19 hooks library for the Figma Variables API: fetch, update, and manage design tokens via the official [Figma REST API](https://www.figma.com/developers/api#variables).
7
+ Built and maintained by Mark Learst.
8
+
9
+ 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](https://www.figma.com/developers/api#variables).
6
10
 
7
- Built for the modern web, this library provides a suite of hooks to fetch, manage, and mutate your design tokens, making it easy to sync them between Figma and your React applications, Storybooks, or design system dashboards.
11
+ 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.
8
12
 
9
13
  ![Status](https://img.shields.io/badge/status-stable-brightgreen)
10
- ![CI](https://github.com/marklearst/figma-vars-hooks/actions/workflows/publish.yml/badge.svg)
14
+ ![CI](https://github.com/marklearst/figma-vars-hooks/actions/workflows/ci.yml/badge.svg)
11
15
  [![codecov](https://codecov.io/gh/marklearst/figma-vars-hooks/branch/main/graph/badge.svg)](https://codecov.io/gh/marklearst/figma-vars-hooks)
12
16
  ![Test Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
13
17
  ![License](https://img.shields.io/github/license/marklearst/figma-vars-hooks)
14
18
  ![GitHub last commit](https://img.shields.io/github/last-commit/marklearst/figma-vars-hooks)
15
19
  ![GitHub code size](https://img.shields.io/github/languages/code-size/marklearst/figma-vars-hooks)
16
- ![TypeScript](https://img.shields.io/badge/TypeScript-Strict-blue?logo=typescript)
17
- ![npm](https://img.shields.io/npm/v/@figma-vars/hooks)
18
- ![npm downloads](https://img.shields.io/npm/dm/@figma-vars/hooks)
19
-
20
- ---
21
20
 
22
- ## ๐Ÿš€ Features
23
-
24
- - **โœ… Token Agnostic for the Best DX**: Our library doesn't care _how_ you get your Figma token. Use environment variables, `localStorage`, a state management library, or even a simple input field. This framework-agnostic approach means it works seamlessly with Vite, Next.js, Create React App, and more, without locking you into a specific build tool.
25
- - **โš›๏ธ Modern React Hooks**: A full suite of hooks for fetching and mutating Figma variables, collections, and modes.
26
- - **โœ๏ธ Ergonomic Mutations**: A `useMutation`-style API for creating, updating, and deleting variables, providing clear loading and error states.
27
- - **๐Ÿ”’ TypeScript-first**: Strictly typed for an ergonomic and safe developer experience. Get autocompletion for all API responses.
28
- - **๐Ÿ“– Storybook & Next.js Ready**: Perfect for building live design token dashboards or style guides.
29
-
30
- ---
21
+ ## ๐Ÿ“Œ Why 3.0
31
22
 
32
- ## ๐Ÿงฑ Architecture Highlights
23
+ - โœจ **New DX Features**: SWR configuration support, error handling utilities, cache invalidation helpers
24
+ - ๐Ÿ”ง **React 19.2 Ready**: Optimized hooks with proper cleanup and stable function references
25
+ - ๐Ÿ›ก๏ธ **Better Error Handling**: `FigmaApiError` class with HTTP status codes for better error differentiation
26
+ - โœ… **Type Safety**: Removed unsafe type assertions, improved type definitions throughout
27
+ - ๐Ÿš€ **Performance**: Hardened SWR usage (stable keys, `null` to disable, cleaner fallback handling)
28
+ - ๐Ÿ“ฆ **Modern Tooling**: Node 20+ toolchain, strict TypeScript, and ESM-first packaging with CJS interop
29
+ - ๐Ÿ–ฅ๏ธ **CLI Export Tool**: Automate variable exports with `figma-vars-export` for CI/CD (Enterprise required)
33
30
 
34
- - โœ… **100% Test Coverage** - Comprehensive test suite with 78 tests covering all hooks, utilities, and edge cases via Vitest
35
- - โœ… **Consistent Error Handling** - Standardized error propagation with clear messages from the Figma API
36
- - โœ… **Strictly Typed APIs** - Modern TypeScript best practices with full type inference and autocompletion
37
- - โœ… **Predictable Hook Signatures** - Consistent, composable patterns designed for safe React integrations
38
- - โœ… **Developer-First Architecture** - Clean folder structure, path aliases, and logical component separation
39
- - โœ… **React Ecosystem** - Built specifically for React apps, Storybook, Next.js, and design system dashboards
40
- - โœ… **Ergonomic DX** - Intuitive API that's easy to use in both prototype and production environments
41
- - โœ… **Minimal Dependencies** - Leverages SWR for caching with careful dependency selection for optimal bundle size
31
+ ## ๐Ÿš€ Features at a Glance
42
32
 
43
- ---
33
+ - **Modern React 19.2 hooks** for variables, collections, modes, and published variables
34
+ - **Ergonomic mutation hooks** with consistent loading/error states
35
+ - **SWR configuration support** for customizing caching and revalidation behavior
36
+ - **Error handling utilities** for type-safe error checking and status code access
37
+ - **Cache invalidation helpers** for automatic data refresh after mutations
38
+ - **CLI export tool** (`figma-vars-export`) for automating variable exports to JSON (Enterprise required)
39
+ - **Fallback JSON support** (object or string) for offline/static use - works without Enterprise!
40
+ - **Typed core entrypoint** for non-React consumers (Axios, TanStack Query, server scripts)
41
+ - **100% test coverage** + strict TypeScript + clean exports/attw/publint/size-limit checks
44
42
 
45
43
  ## ๐Ÿ“ฆ Install
46
44
 
47
45
  ```bash
48
46
  npm install @figma-vars/hooks
49
47
  # or
50
- yarn add @figma-vars/hooks
51
- # or
52
48
  pnpm add @figma-vars/hooks
53
49
  ```
54
50
 
55
- > **Peer dependencies:** You'll need `react` and `react-dom`.
51
+ Peer deps: `react` and `react-dom`.
56
52
 
57
- ---
53
+ ## ๐Ÿ–ฅ๏ธ CLI Export Tool
54
+
55
+ 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.
56
+
57
+ > โš ๏ธ **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](#exporting-variables-for-fallback) method instead.
58
+
59
+ ```bash
60
+ # Using npx (no install needed)
61
+ FIGMA_TOKEN=your_token npx figma-vars-export --file-key YOUR_FILE_KEY --out ./variables.json
58
62
 
59
- ## ๐Ÿ› ๏ธ Setup & Usage
63
+ # After installing
64
+ npm install @figma-vars/hooks
65
+ FIGMA_TOKEN=your_token figma-vars-export --file-key YOUR_FILE_KEY --out ./variables.json
66
+
67
+ # Show help
68
+ figma-vars-export --help
69
+ ```
70
+
71
+ **Options:**
72
+
73
+ - `--file-key` - Figma file key (required, or set `FIGMA_FILE_KEY` env var)
74
+ - `--out` - Output path (default: `data/figma-variables.json`)
75
+ - `--help` - Show help message
76
+
77
+ **Environment Variables:**
78
+
79
+ - `FIGMA_TOKEN` or `FIGMA_PAT` - Figma Personal Access Token (required)
80
+ - `FIGMA_FILE_KEY` - Figma file key (optional)
60
81
 
61
- The library is designed to be as flexible as possible. You provide the Figma token and file key, and the hooks handle the rest.
82
+ **Example Output:**
62
83
 
63
- Wrap your application (or the relevant component tree) with the `FigmaVarsProvider`. This makes the Figma token and file key available to all the hooks.
84
+ ```
85
+ Saved variables to ./variables.json
86
+ Variables count: 42
87
+ ```
88
+
89
+ **No Enterprise?** See [Exporting variables for fallback](#exporting-variables-for-fallback) for alternative methods that work without Enterprise.
90
+
91
+ ## ๐Ÿ› ๏ธ Quick Start (SWR-powered hooks)
64
92
 
65
93
  ```tsx
66
- // src/main.tsx or App.tsx
67
- import React from 'react'
68
- import ReactDOM from 'react-dom/client'
69
- import { FigmaVarsProvider } from '@figma-vars/hooks'
70
- import App from './App'
94
+ import { FigmaVarsProvider, useVariables } from '@figma-vars/hooks'
71
95
 
72
- // The token can come from anywhere: .env, localStorage, state, etc.
73
96
  const FIGMA_TOKEN = import.meta.env.VITE_FIGMA_TOKEN
74
- const FIGMA_FILE_KEY = 'your-figma-file-key'
97
+ const FIGMA_FILE_KEY = 'your-file-key'
75
98
 
76
- ReactDOM.createRoot(document.getElementById('root')!).render(
77
- <React.StrictMode>
99
+ function App() {
100
+ return (
78
101
  <FigmaVarsProvider
79
102
  token={FIGMA_TOKEN}
80
- fileKey={FIGMA_FILE_KEY}>
81
- <App />
103
+ fileKey={FIGMA_FILE_KEY}
104
+ swrConfig={{
105
+ revalidateOnFocus: false,
106
+ dedupingInterval: 5000,
107
+ }}>
108
+ <Tokens />
82
109
  </FigmaVarsProvider>
83
- </React.StrictMode>
84
- )
110
+ )
111
+ }
112
+
113
+ function Tokens() {
114
+ const { data, isLoading, error } = useVariables()
115
+ if (isLoading) return <div>Loadingโ€ฆ</div>
116
+ if (error) return <div>Error: {error.message}</div>
117
+ const variables = Object.values(data?.meta.variables ?? {})
118
+ return <pre>{JSON.stringify(variables, null, 2)}</pre>
119
+ }
85
120
  ```
86
121
 
87
- ### Fetching Data
122
+ ## ๐Ÿงฉ Non-SWR Usage (Core entrypoint)
88
123
 
89
- Now, you can use the query hooks anywhere in your app:
124
+ Use the `/core` build when you prefer Axios/TanStack/server scripts without React/SWR.
90
125
 
91
- ```tsx
92
- // src/components/TokenList.tsx
93
- import { useVariables } from '@figma-vars/hooks'
126
+ **Axios example (GET + bulk PUT)**
94
127
 
95
- export function TokenList() {
96
- const { data, isLoading, error } = useVariables()
128
+ ```ts
129
+ import axios from 'axios'
130
+ import { FIGMA_FILE_VARIABLES_PATH } from '@figma-vars/hooks/core'
97
131
 
98
- if (isLoading) return <div>Loading tokens...</div>
99
- if (error) return <div>Error: {error.message}</div>
132
+ const token = process.env.FIGMA_TOKEN!
133
+ const fileKey = process.env.FIGMA_FILE_KEY!
100
134
 
101
- // The 'data' object contains variables, collections, and modes
102
- const variables = Object.values(data?.variables ?? {})
135
+ // Fetch local variables
136
+ const url = `https://api.figma.com${FIGMA_FILE_VARIABLES_PATH(fileKey)}/local`
137
+ const { data } = await axios.get(url, {
138
+ headers: { 'X-FIGMA-TOKEN': token, 'Content-Type': 'application/json' },
139
+ })
103
140
 
104
- return (
105
- <ul>
106
- {variables.map((variable) => (
107
- <li key={variable.id}>
108
- {variable.name}: {JSON.stringify(variable.valuesByMode)}
109
- </li>
110
- ))}
111
- </ul>
112
- )
141
+ // Bulk update
142
+ await axios.put(
143
+ `https://api.figma.com${FIGMA_FILE_VARIABLES_PATH(fileKey)}`,
144
+ { variables: [{ action: 'UPDATE', id: 'VariableId:123', name: 'new-name' }] },
145
+ { headers: { 'X-FIGMA-TOKEN': token, 'Content-Type': 'application/json' } }
146
+ )
147
+ ```
148
+
149
+ **TanStack Query example**
150
+
151
+ ```ts
152
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
153
+ import { FIGMA_FILE_VARIABLES_PATH, fetcher, mutator } from '@figma-vars/hooks/core'
154
+
155
+ const token = process.env.FIGMA_TOKEN!
156
+ const fileKey = process.env.FIGMA_FILE_KEY!
157
+
158
+ export function useLocalVariables() {
159
+ return useQuery({
160
+ queryKey: ['figma-local', fileKey],
161
+ queryFn: () => fetcher(`${FIGMA_FILE_VARIABLES_PATH(fileKey)}/local`, token),
162
+ staleTime: 60_000,
163
+ })
113
164
  }
165
+
166
+ export function useBulkUpdate() {
167
+ const qc = useQueryClient()
168
+ return useMutation({
169
+ mutationFn: (payload: unknown) =>
170
+ mutator(FIGMA_FILE_VARIABLES_PATH(fileKey), token, 'UPDATE', payload),
171
+ onSuccess: () => qc.invalidateQueries({ queryKey: ['figma-local', fileKey] }),
172
+ })
173
+ }
174
+ ```
175
+
176
+ ## ๐Ÿ›ก๏ธ Fallback JSON (offline/static)
177
+
178
+ Pass `fallbackFile` (object or JSON string) to `FigmaVarsProvider` to bypass live API calls:
179
+
180
+ ```tsx
181
+ import exportedVariables from './figma-variables.json'
182
+ ;<FigmaVarsProvider
183
+ token={null}
184
+ fileKey={null}
185
+ fallbackFile={exportedVariables}>
186
+ <App />
187
+ </FigmaVarsProvider>
114
188
  ```
115
189
 
116
- ### Mutating Data
190
+ ### Exporting variables for fallback
191
+
192
+ There are several ways to get your Figma variables as JSON:
193
+
194
+ 1. **Dev Mode / plugin export (recommended, no Enterprise needed)** โญ
195
+ - Use a Variables exporter plugin in Figma Dev Mode to download the full Variables panel as JSON
196
+ - Save anywhere (e.g., `data/figma-variables.json`) and pass it to `fallbackFile`
197
+ - Works for everyone, no Enterprise account required!
198
+
199
+ 2. **CLI export tool (Enterprise required)** ๐Ÿš€
200
+ - Automatically exports via REST API - perfect for CI/CD and automation
201
+ - See the [CLI Export Tool](#-cli-export-tool) section above for full usage details
202
+ - Also available from cloned repo: `node scripts/export-variables.mjs --file-key KEY --out file.json`
117
203
 
118
- To create, update, or delete variables, use the provided mutation hooks. They follow a standard pattern, returning a `mutate` function and states for `data`, `isLoading`, and `error`.
204
+ - **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.
119
205
 
120
- Here's an example of creating a new variable:
206
+ 4. **Style Dictionary**
207
+ - Once you have the JSON (from any path), feed it into Style Dictionary to emit platform-specific artifacts
208
+ - Or import it directly via `fallbackFile`
209
+
210
+ ## ๐Ÿ”ง Mutation Hooks (verbs fixed)
211
+
212
+ - `useCreateVariable` โ†’ POST via bulk endpoint with `action: 'CREATE'`
213
+ - `useUpdateVariable` โ†’ PUT via bulk endpoint with `action: 'UPDATE'`
214
+ - `useDeleteVariable` โ†’ DELETE via bulk endpoint with `action: 'DELETE'`
215
+ - `useBulkUpdateVariables` โ†’ PUT bulk payload (collections, modes, variables, values)
216
+
217
+ All return `{ mutate, data, error, isLoading, isSuccess, isError }`.
218
+
219
+ ### Example: Creating and updating variables
121
220
 
122
221
  ```tsx
123
- // src/components/CreateVariableForm.tsx
124
- import { useCreateVariable } from '@figma-vars/hooks'
125
- import type { CreateVariablePayload } from '@figma-vars/hooks'
126
-
127
- function CreateVariableForm({ collectionId }: { collectionId: string }) {
128
- const { mutate, data, isLoading, error } = useCreateVariable()
129
-
130
- const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
131
- event.preventDefault()
132
- const form = event.currentTarget
133
- const variableName = (
134
- form.elements.namedItem('variableName') as HTMLInputElement
135
- )?.value
136
-
137
- if (!variableName) return
138
-
139
- const payload: CreateVariablePayload = {
140
- name: variableName,
141
- variableCollectionId: collectionId,
142
- resolvedType: 'COLOR', // Example type
143
- }
144
- mutate(payload)
222
+ import { useCreateVariable, useUpdateVariable, useInvalidateVariables } from '@figma-vars/hooks'
223
+
224
+ function VariableEditor() {
225
+ const { mutate: create } = useCreateVariable()
226
+ const { mutate: update } = useUpdateVariable()
227
+ const { invalidate } = useInvalidateVariables()
228
+
229
+ const handleCreate = async () => {
230
+ await create({
231
+ name: 'Primary Color',
232
+ variableCollectionId: 'CollectionId:123',
233
+ resolvedType: 'COLOR',
234
+ })
235
+ invalidate() // Refresh cache after mutation
236
+ }
237
+
238
+ const handleUpdate = async (id: string) => {
239
+ await update({
240
+ variableId: id,
241
+ payload: { name: 'Updated Name' },
242
+ })
243
+ invalidate() // Refresh cache after mutation
145
244
  }
146
245
 
147
246
  return (
148
- <form onSubmit={handleSubmit}>
149
- <input
150
- name="variableName"
151
- placeholder="New variable name"
152
- />
153
- <button
154
- type="submit"
155
- disabled={isLoading}>
156
- {isLoading ? 'Creating...' : 'Create Variable'}
157
- </button>
158
- {error && <p>Error: {error.message}</p>}
159
- {data && <p>Created variable with ID: {data.id}</p>}
160
- </form>
247
+ <>
248
+ <button onClick={handleCreate}>Create Variable</button>
249
+ <button onClick={() => handleUpdate('VariableId:123')}>Update</button>
250
+ </>
161
251
  )
162
252
  }
163
253
  ```
164
254
 
165
- ### Figma PAT Security
255
+ ## ๐Ÿ›ก๏ธ Error Handling
166
256
 
167
- When using the Figma API, it's essential to keep your Personal Access Token (PAT) secure. Here are some best practices:
257
+ 3.0.0 introduces powerful error handling utilities for type-safe error checking:
168
258
 
169
- - Never hardcode your PAT in your code.
170
- - Use environment variables or a secure storage mechanism to store your PAT.
171
- - Limit the scope of your PAT to only the necessary permissions.
172
- - Rotate your PAT regularly.
259
+ ```tsx
260
+ import { isFigmaApiError, getErrorStatus, getErrorMessage, hasErrorStatus } from '@figma-vars/hooks'
173
261
 
174
- ### Advanced Usage
262
+ function ErrorHandler({ error }: { error: Error | null }) {
263
+ if (!error) return null
175
264
 
176
- For advanced use cases, you can use the `useFigmaToken` hook to access the token and file key from the context.
265
+ // Type guard for FigmaApiError
266
+ if (isFigmaApiError(error)) {
267
+ const status = error.statusCode
177
268
 
178
- ```tsx
179
- // src/components/AdvancedUsage.tsx
180
- import { useFigmaToken } from '@figma-vars/hooks'
181
-
182
- function AdvancedUsage() {
183
- const { token, fileKey } = useFigmaToken()
184
-
185
- // Use the token and file key to make custom API requests
186
- const apiRequest = async () => {
187
- const response = await fetch(
188
- `https://api.figma.com/v1/files/${fileKey}/variables`,
189
- {
190
- headers: {
191
- 'X-Figma-Token': token,
192
- },
193
- }
194
- )
269
+ if (status === 401) {
270
+ return <div>Authentication required. Please check your token.</div>
271
+ }
272
+ if (status === 403) {
273
+ return <div>Access forbidden. Check file permissions.</div>
274
+ }
275
+ if (status === 429) {
276
+ return <div>Rate limit exceeded. Please wait before retrying.</div>
277
+ }
278
+ if (status === 404) {
279
+ return <div>File or variable not found.</div>
280
+ }
281
+ }
282
+
283
+ // Helper functions
284
+ const status = getErrorStatus(error) // number | null
285
+ const message = getErrorMessage(error) // string
195
286
 
196
- const data = await response.json()
197
- console.log(data)
287
+ // Convenience check
288
+ if (hasErrorStatus(error, 401)) {
289
+ // Handle unauthorized
198
290
  }
199
291
 
200
- return <button onClick={apiRequest}>Make API Request</button>
292
+ return <div>Error: {message}</div>
201
293
  }
202
294
  ```
203
295
 
204
- ### Error Handling
296
+ **Common HTTP Status Codes:**
297
+
298
+ - `401` - Unauthorized (invalid or missing token)
299
+ - `403` - Forbidden (insufficient permissions)
300
+ - `404` - Not Found (file/variable doesn't exist)
301
+ - `429` - Too Many Requests (rate limit exceeded)
302
+
303
+ ## ๐Ÿ”„ Cache Management
205
304
 
206
- All hooks return an `error` state that you can use to handle errors.
305
+ After mutations, use `useInvalidateVariables` to refresh cached data:
207
306
 
208
307
  ```tsx
209
- // src/components/ErrorHandling.tsx
210
- import { useVariables } from '@figma-vars/hooks'
308
+ import { useUpdateVariable, useInvalidateVariables } from '@figma-vars/hooks'
211
309
 
212
- function ErrorHandling() {
213
- const { data, isLoading, error } = useVariables()
310
+ function UpdateButton({ variableId }: { variableId: string }) {
311
+ const { mutate, isLoading } = useUpdateVariable()
312
+ const { invalidate, revalidate } = useInvalidateVariables()
313
+
314
+ const handleUpdate = async () => {
315
+ await mutate({
316
+ variableId,
317
+ payload: { name: 'New Name' },
318
+ })
319
+
320
+ // Option 1: Invalidate (refetch on next access)
321
+ invalidate()
214
322
 
215
- if (error) {
216
- return (
217
- <div>
218
- <h2>Error</h2>
219
- <p>{error.message}</p>
220
- </div>
221
- )
323
+ // Option 2: Revalidate immediately (refetch now)
324
+ // revalidate()
222
325
  }
223
326
 
224
- // Render data or loading state
327
+ return (
328
+ <button
329
+ onClick={handleUpdate}
330
+ disabled={isLoading}>
331
+ {isLoading ? 'Updating...' : 'Update Variable'}
332
+ </button>
333
+ )
225
334
  }
226
335
  ```
227
336
 
228
- ---
337
+ ## โš™๏ธ SWR Configuration
338
+
339
+ Customize SWR behavior globally through the provider:
229
340
 
230
- ## ๐Ÿงฉ API Reference
341
+ ```tsx
342
+ <FigmaVarsProvider
343
+ token={FIGMA_TOKEN}
344
+ fileKey={FIGMA_FILE_KEY}
345
+ swrConfig={{
346
+ revalidateOnFocus: false, // Don't refetch on window focus
347
+ dedupingInterval: 5000, // Dedupe requests within 5s
348
+ errorRetryCount: 3, // Retry failed requests 3 times
349
+ errorRetryInterval: 1000, // Wait 1s between retries
350
+ onError: error => {
351
+ // Global error handler
352
+ if (isFigmaApiError(error) && error.statusCode === 429) {
353
+ console.warn('Rate limited, backing off...')
354
+ }
355
+ },
356
+ }}>
357
+ <App />
358
+ </FigmaVarsProvider>
359
+ ```
231
360
 
232
- ### Core Hooks
361
+ **Common SWR Options:**
233
362
 
234
- - `useVariables()`: Fetches all local variables for the file key provided to the `FigmaVarsProvider`. Returns a SWR hook state with `data`, `isLoading`, and `error` properties. The actual Figma response is in `data.data`.
235
- - `useVariableCollections()`: A convenience hook that returns just the variable collections from the main `useVariables` data.
236
- - `useVariableModes()`: A convenience hook that returns just the variable modes from the main `useVariables` data.
237
- - `useFigmaToken()`: A simple hook to access the token and file key from the context.
363
+ - `revalidateOnFocus` - Refetch when window regains focus (default: `true`)
364
+ - `dedupingInterval` - Deduplication interval in ms (default: `2000`)
365
+ - `errorRetryCount` - Max retry attempts (default: `5`)
366
+ - `refreshInterval` - Polling interval in ms (default: `0` = disabled)
367
+ - `onError` - Global error callback
238
368
 
239
- ### Mutation Hooks
369
+ ## ๐Ÿ“š API Cheat Sheet
240
370
 
241
- All mutation hooks return an object with the following shape: `{ mutate, data, isLoading, error }`.
371
+ ### Hooks
242
372
 
243
- - `useCreateVariable()`: Creates a new variable. The `mutate` function expects a `CreateVariablePayload` object.
244
- - `useUpdateVariable()`: Updates an existing variable. The `mutate` function expects an object `{ variableId, payload }` where `payload` is an `UpdateVariablePayload`.
245
- - `useDeleteVariable()`: Deletes a variable. The `mutate` function expects the `variableId` (string) of the variable to delete.
246
- - `useBulkUpdateVariables()`: Creates, updates, or deletes multiple entities in a single batch operation. The `mutate` function expects a `BulkUpdatePayload` object.
373
+ - **Queries**: `useVariables` (local), `usePublishedVariables` (library/published), `useVariableCollections`, `useVariableModes`, `useFigmaToken`
374
+ - **Mutations**: `useCreateVariable`, `useUpdateVariable`, `useDeleteVariable`, `useBulkUpdateVariables`
375
+ - **Cache**: `useInvalidateVariables` (invalidate/revalidate cache)
376
+
377
+ ### Utilities
378
+
379
+ - **Filtering**: `filterVariables` (filter by type, name, etc.)
380
+ - **Error Handling**: `isFigmaApiError`, `getErrorStatus`, `getErrorMessage`, `hasErrorStatus`
381
+ - **Core helpers**: `fetcher`, `mutator`, constants for endpoints and headers
247
382
 
248
383
  ### Types
249
384
 
250
- All types are exported from `@figma-vars/hooks`. The core response type from Figma for local variables is `LocalVariablesResponse`.
385
+ - **Responses**: `LocalVariablesResponse`, `PublishedVariablesResponse`
386
+ - **Variables**: `FigmaVariable`, `FigmaCollection`, `VariableMode`
387
+ - **Mutations**: `BulkUpdatePayload`, `CreateVariablePayload`, `UpdateVariablePayload`
388
+ - **Errors**: `FigmaApiError` (extends `Error` with `statusCode`)
251
389
 
252
- ---
390
+ ## ๐Ÿ” Auth & Scope
391
+
392
+ - Header: `X-FIGMA-TOKEN: <PAT>`
393
+ - Scopes: `file_variables:read` for GETs, `file_variables:write` for mutations.
394
+ - Enterprise Full seat required for live API; fallback JSON works without a token.
395
+
396
+ ## โš ๏ธ Enterprise Requirement and Offline Options
397
+
398
+ - The Figma Variables REST API requires a Figma Enterprise seat for live requests. Without Enterprise, live calls will fail even with a valid PAT.
399
+ - 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.
400
+ - 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.
401
+
402
+ ## ๐Ÿšซ Do Not Publish Tokens or File Keys
253
403
 
254
- ## ๐Ÿ“š Storybook & Next.js Integration
404
+ - Never commit PATs or file keys to git, Storybook static builds, or client bundles.
405
+ - Use environment variables (`process.env` / `import.meta.env`) and secret managers; keep them server-side where possible.
406
+ - Prefer `fallbackFile` with `token={null}`/`fileKey={null}` for demos and public Storybooks.
407
+ - Avoid logging tokens or keys; scrub them from error messages and analytics.
255
408
 
256
- The provider model makes integration trivial. Simply wrap your Storybook stories or Next.js pages with the `FigmaVarsProvider`.
409
+ ## ๐Ÿ“ˆ Rate Limits
410
+
411
+ - Figma enforces per-token limits. Rely on SWR/TanStack caching, avoid unnecessary refetches, and prefer fallback JSON for static sites.
412
+ - Use `swrConfig` to customize `dedupingInterval` and `errorRetryCount` to optimize API usage.
413
+ - Handle `429` rate limit errors with `isFigmaApiError` and implement exponential backoff if needed.
414
+
415
+ ## ๐Ÿ“š Storybook & Next.js
416
+
417
+ - **Storybook decorator**: wrap stories once so hooks have context and tokens.
257
418
 
258
419
  ```tsx
259
- // In a Storybook story
260
- import { FigmaVarsProvider, useVariables } from '@figma-vars/hooks'
420
+ // .storybook/preview.tsx
421
+ import { FigmaVarsProvider } from '@figma-vars/hooks'
422
+ import type { Preview } from '@storybook/react'
423
+
424
+ const FIGMA_TOKEN = process.env.STORYBOOK_FIGMA_TOKEN
425
+ const FIGMA_FILE_KEY = process.env.STORYBOOK_FIGMA_FILE_KEY
426
+
427
+ const preview: Preview = {
428
+ decorators: [
429
+ Story => (
430
+ <FigmaVarsProvider
431
+ token={FIGMA_TOKEN}
432
+ fileKey={FIGMA_FILE_KEY}>
433
+ <Story />
434
+ </FigmaVarsProvider>
435
+ ),
436
+ ],
437
+ }
261
438
 
262
- export const TokensStory = () => (
263
- <FigmaVarsProvider
264
- token="YOUR_TOKEN"
265
- fileKey="YOUR_FILE_KEY">
266
- <TokenList />
267
- </FigmaVarsProvider>
268
- )
439
+ export default preview
440
+ ```
441
+
442
+ - **Next.js App Router**: provide context in a shared provider file.
269
443
 
270
- const TokenList = () => {
271
- const { data } = useVariables()
272
- return <pre>{JSON.stringify(data?.variables, null, 2)}</pre>
444
+ ```tsx
445
+ // app/providers.tsx
446
+ import { FigmaVarsProvider } from '@figma-vars/hooks'
447
+
448
+ export function Providers({ children }: { children: React.ReactNode }) {
449
+ return (
450
+ <FigmaVarsProvider
451
+ token={process.env.NEXT_PUBLIC_FIGMA_TOKEN}
452
+ fileKey={process.env.NEXT_PUBLIC_FIGMA_FILE_KEY}>
453
+ {children}
454
+ </FigmaVarsProvider>
455
+ )
273
456
  }
274
457
  ```
275
458
 
459
+ ## ๐Ÿงช Tooling & Quality Gates
460
+
461
+ - `pnpm run build`, `pnpm test`, `pnpm run test:coverage`
462
+ - `pnpm run check:publint`, `pnpm run check:attw`, `pnpm run check:size`
463
+
464
+ ## ๐Ÿงญ Release Checklist (for 3.0.0)
465
+
466
+ - Run `pnpm run check:release`
467
+ - Tag `v3.0.0` (CI publishes to npm)
468
+ - Update dist-tags on npm if needed (`latest` โ†’ 3.0.0)
469
+
276
470
  ---
277
471
 
278
472
  ## ๐Ÿ“ Contributing
@@ -282,4 +476,4 @@ PRs and issues are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for gu
282
476
  ## ๐Ÿ“ License
283
477
 
284
478
  This project is licensed under the [MIT License](LICENSE).
285
- ยฉ 2024โ€“2025 Mark Learst
479
+ ยฉ 2024โ€“2026 Mark Learst
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/api/fetcher.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,OAAO,CAAC,SAAS,GAAG,OAAO,EAC/C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,CAAC,CA2BpB"}
1
+ {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/api/fetcher.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,OAAO,CAAC,SAAS,GAAG,OAAO,EAC/C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,CAAC,CAyCpB"}