@figma-vars/hooks 2.0.0-beta.3 โ 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.
- package/README.md +355 -220
- package/dist/api/fetcher.d.ts.map +1 -1
- package/dist/api/mutator.d.ts +10 -9
- package/dist/api/mutator.d.ts.map +1 -1
- package/dist/constants/index.d.ts +2 -28
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/contexts/FigmaVarsProvider.d.ts +1 -1
- package/dist/contexts/FigmaVarsProvider.d.ts.map +1 -1
- package/dist/core/index.d.cts +8 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core.cjs +1 -0
- package/dist/core.d.cts +2 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.mjs +20 -0
- package/dist/hooks/index.d.ts +19 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useBulkUpdateVariables.d.ts.map +1 -1
- package/dist/hooks/useCreateVariable.d.ts.map +1 -1
- package/dist/hooks/useDeleteVariable.d.ts.map +1 -1
- package/dist/hooks/useInvalidateVariables.d.ts +31 -0
- package/dist/hooks/useInvalidateVariables.d.ts.map +1 -0
- package/dist/hooks/usePublishedVariables.d.ts +42 -0
- package/dist/hooks/usePublishedVariables.d.ts.map +1 -0
- package/dist/hooks/useUpdateVariable.d.ts.map +1 -1
- package/dist/hooks/useVariables.d.ts.map +1 -1
- package/dist/index-BIUpDTdr.cjs +1 -0
- package/dist/index-Cd4HQQHO.js +94 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +229 -165
- package/dist/types/contexts.d.ts +29 -3
- package/dist/types/contexts.d.ts.map +1 -1
- package/dist/types/figma.d.ts +53 -0
- package/dist/types/figma.d.ts.map +1 -1
- package/dist/types/mutations.d.ts +1 -1
- package/dist/types/mutations.d.ts.map +1 -1
- package/dist/utils/errorHelpers.d.ts +96 -0
- package/dist/utils/errorHelpers.d.ts.map +1 -0
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +67 -32
- package/scripts/export-variables.mjs +101 -0
- package/dist/index.d.mts +0 -2
package/README.md
CHANGED
|
@@ -1,337 +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
|
-
|
|
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
|

|
|
10
|
-

|
|
11
15
|
[](https://codecov.io/gh/marklearst/figma-vars-hooks)
|
|
12
16
|

|
|
13
17
|

|
|
14
18
|

|
|
15
19
|

|
|
16
|
-

|
|
17
|
-

|
|
18
|
-

|
|
19
|
-
|
|
20
|
-
---
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## ๐ Why 3.0
|
|
23
22
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
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)
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## ๐งฑ Architecture Highlights
|
|
31
|
+
## ๐ Features at a Glance
|
|
35
32
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
---
|
|
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
|
|
46
42
|
|
|
47
43
|
## ๐ฆ Install
|
|
48
44
|
|
|
49
45
|
```bash
|
|
50
46
|
npm install @figma-vars/hooks
|
|
51
47
|
# or
|
|
52
|
-
yarn add @figma-vars/hooks
|
|
53
|
-
# or
|
|
54
48
|
pnpm add @figma-vars/hooks
|
|
55
49
|
```
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
---
|
|
51
|
+
Peer deps: `react` and `react-dom`.
|
|
60
52
|
|
|
61
|
-
##
|
|
53
|
+
## ๐ฅ๏ธ CLI Export Tool
|
|
62
54
|
|
|
63
|
-
The
|
|
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.
|
|
64
56
|
|
|
65
|
-
|
|
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.
|
|
66
58
|
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
import ReactDOM from 'react-dom/client'
|
|
71
|
-
import { FigmaVarsProvider } from '@figma-vars/hooks'
|
|
72
|
-
import App from './App'
|
|
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
|
|
73
62
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
77
66
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<FigmaVarsProvider
|
|
81
|
-
token={FIGMA_TOKEN}
|
|
82
|
-
fileKey={FIGMA_FILE_KEY}>
|
|
83
|
-
<App />
|
|
84
|
-
</FigmaVarsProvider>
|
|
85
|
-
</React.StrictMode>
|
|
86
|
-
)
|
|
67
|
+
# Show help
|
|
68
|
+
figma-vars-export --help
|
|
87
69
|
```
|
|
88
70
|
|
|
89
|
-
|
|
71
|
+
**Options:**
|
|
90
72
|
|
|
91
|
-
|
|
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
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
// src/components/TokenList.tsx
|
|
95
|
-
import { useVariables } from '@figma-vars/hooks'
|
|
77
|
+
**Environment Variables:**
|
|
96
78
|
|
|
97
|
-
|
|
98
|
-
|
|
79
|
+
- `FIGMA_TOKEN` or `FIGMA_PAT` - Figma Personal Access Token (required)
|
|
80
|
+
- `FIGMA_FILE_KEY` - Figma file key (optional)
|
|
99
81
|
|
|
100
|
-
|
|
101
|
-
if (error) return <div>Error: {error.message}</div>
|
|
82
|
+
**Example Output:**
|
|
102
83
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<ul>
|
|
108
|
-
{variables.map(variable => (
|
|
109
|
-
<li key={variable.id}>
|
|
110
|
-
{variable.name}: {JSON.stringify(variable.valuesByMode)}
|
|
111
|
-
</li>
|
|
112
|
-
))}
|
|
113
|
-
</ul>
|
|
114
|
-
)
|
|
115
|
-
}
|
|
84
|
+
```
|
|
85
|
+
Saved variables to ./variables.json
|
|
86
|
+
Variables count: 42
|
|
116
87
|
```
|
|
117
88
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
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`.
|
|
89
|
+
**No Enterprise?** See [Exporting variables for fallback](#exporting-variables-for-fallback) for alternative methods that work without Enterprise.
|
|
121
90
|
|
|
122
|
-
|
|
91
|
+
## ๐ ๏ธ Quick Start (SWR-powered hooks)
|
|
123
92
|
|
|
124
93
|
```tsx
|
|
125
|
-
|
|
126
|
-
import { useCreateVariable } from '@figma-vars/hooks'
|
|
127
|
-
import type { CreateVariablePayload } from '@figma-vars/hooks'
|
|
128
|
-
|
|
129
|
-
function CreateVariableForm({ collectionId }: { collectionId: string }) {
|
|
130
|
-
const { mutate, data, isLoading, error } = useCreateVariable()
|
|
131
|
-
|
|
132
|
-
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
133
|
-
event.preventDefault()
|
|
134
|
-
const form = event.currentTarget
|
|
135
|
-
const variableName = (form.elements.namedItem('variableName') as HTMLInputElement)?.value
|
|
136
|
-
|
|
137
|
-
if (!variableName) return
|
|
94
|
+
import { FigmaVarsProvider, useVariables } from '@figma-vars/hooks'
|
|
138
95
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
variableCollectionId: collectionId,
|
|
142
|
-
resolvedType: 'COLOR', // Example type
|
|
143
|
-
}
|
|
144
|
-
mutate(payload)
|
|
145
|
-
}
|
|
96
|
+
const FIGMA_TOKEN = import.meta.env.VITE_FIGMA_TOKEN
|
|
97
|
+
const FIGMA_FILE_KEY = 'your-file-key'
|
|
146
98
|
|
|
99
|
+
function App() {
|
|
147
100
|
return (
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
</button>
|
|
158
|
-
{error && <p>Error: {error.message}</p>}
|
|
159
|
-
{data && <p>Created variable with ID: {data.id}</p>}
|
|
160
|
-
</form>
|
|
101
|
+
<FigmaVarsProvider
|
|
102
|
+
token={FIGMA_TOKEN}
|
|
103
|
+
fileKey={FIGMA_FILE_KEY}
|
|
104
|
+
swrConfig={{
|
|
105
|
+
revalidateOnFocus: false,
|
|
106
|
+
dedupingInterval: 5000,
|
|
107
|
+
}}>
|
|
108
|
+
<Tokens />
|
|
109
|
+
</FigmaVarsProvider>
|
|
161
110
|
)
|
|
162
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
|
+
}
|
|
163
120
|
```
|
|
164
121
|
|
|
165
|
-
|
|
122
|
+
## ๐งฉ Non-SWR Usage (Core entrypoint)
|
|
123
|
+
|
|
124
|
+
Use the `/core` build when you prefer Axios/TanStack/server scripts without React/SWR.
|
|
125
|
+
|
|
126
|
+
**Axios example (GET + bulk PUT)**
|
|
166
127
|
|
|
167
|
-
|
|
128
|
+
```ts
|
|
129
|
+
import axios from 'axios'
|
|
130
|
+
import { FIGMA_FILE_VARIABLES_PATH } from '@figma-vars/hooks/core'
|
|
168
131
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
- **CI/CD Pipelines**: Use exported JSON files in automated workflows
|
|
172
|
-
- **Team Collaboration**: Share design tokens without API access
|
|
132
|
+
const token = process.env.FIGMA_TOKEN!
|
|
133
|
+
const fileKey = process.env.FIGMA_FILE_KEY!
|
|
173
134
|
|
|
174
|
-
|
|
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
|
+
})
|
|
175
140
|
|
|
176
|
-
|
|
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
|
+
```
|
|
177
148
|
|
|
178
|
-
|
|
179
|
-
2. Open your Figma file in Dev Mode
|
|
180
|
-
3. Run the plugin and export your variables as JSON
|
|
181
|
-
4. Save the JSON file to your project (e.g., `src/assets/figma-variables.json`)
|
|
149
|
+
**TanStack Query example**
|
|
182
150
|
|
|
183
|
-
|
|
151
|
+
```ts
|
|
152
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
153
|
+
import { FIGMA_FILE_VARIABLES_PATH, fetcher, mutator } from '@figma-vars/hooks/core'
|
|
184
154
|
|
|
185
|
-
|
|
155
|
+
const token = process.env.FIGMA_TOKEN!
|
|
156
|
+
const fileKey = process.env.FIGMA_FILE_KEY!
|
|
186
157
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
+
})
|
|
164
|
+
}
|
|
194
165
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
fileKey
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
</React.StrictMode>
|
|
204
|
-
)
|
|
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
|
+
}
|
|
205
174
|
```
|
|
206
175
|
|
|
207
|
-
|
|
176
|
+
## ๐ก๏ธ Fallback JSON (offline/static)
|
|
177
|
+
|
|
178
|
+
Pass `fallbackFile` (object or JSON string) to `FigmaVarsProvider` to bypass live API calls:
|
|
208
179
|
|
|
209
180
|
```tsx
|
|
210
|
-
import
|
|
181
|
+
import exportedVariables from './figma-variables.json'
|
|
211
182
|
;<FigmaVarsProvider
|
|
212
183
|
token={null}
|
|
213
184
|
fileKey={null}
|
|
214
|
-
fallbackFile={
|
|
185
|
+
fallbackFile={exportedVariables}>
|
|
215
186
|
<App />
|
|
216
187
|
</FigmaVarsProvider>
|
|
217
188
|
```
|
|
218
189
|
|
|
219
|
-
###
|
|
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`
|
|
203
|
+
|
|
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.
|
|
205
|
+
|
|
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`
|
|
220
209
|
|
|
221
|
-
|
|
210
|
+
## ๐ง Mutation Hooks (verbs fixed)
|
|
222
211
|
|
|
223
|
-
-
|
|
224
|
-
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
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)
|
|
227
216
|
|
|
228
|
-
|
|
217
|
+
All return `{ mutate, data, error, isLoading, isSuccess, isError }`.
|
|
229
218
|
|
|
230
|
-
|
|
219
|
+
### Example: Creating and updating variables
|
|
231
220
|
|
|
232
221
|
```tsx
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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',
|
|
245
234
|
})
|
|
235
|
+
invalidate() // Refresh cache after mutation
|
|
236
|
+
}
|
|
246
237
|
|
|
247
|
-
|
|
248
|
-
|
|
238
|
+
const handleUpdate = async (id: string) => {
|
|
239
|
+
await update({
|
|
240
|
+
variableId: id,
|
|
241
|
+
payload: { name: 'Updated Name' },
|
|
242
|
+
})
|
|
243
|
+
invalidate() // Refresh cache after mutation
|
|
249
244
|
}
|
|
250
245
|
|
|
251
|
-
return
|
|
246
|
+
return (
|
|
247
|
+
<>
|
|
248
|
+
<button onClick={handleCreate}>Create Variable</button>
|
|
249
|
+
<button onClick={() => handleUpdate('VariableId:123')}>Update</button>
|
|
250
|
+
</>
|
|
251
|
+
)
|
|
252
252
|
}
|
|
253
253
|
```
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
## ๐ก๏ธ Error Handling
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
3.0.0 introduces powerful error handling utilities for type-safe error checking:
|
|
258
258
|
|
|
259
259
|
```tsx
|
|
260
|
-
|
|
261
|
-
import { useVariables } from '@figma-vars/hooks'
|
|
260
|
+
import { isFigmaApiError, getErrorStatus, getErrorMessage, hasErrorStatus } from '@figma-vars/hooks'
|
|
262
261
|
|
|
263
|
-
function
|
|
264
|
-
|
|
262
|
+
function ErrorHandler({ error }: { error: Error | null }) {
|
|
263
|
+
if (!error) return null
|
|
264
|
+
|
|
265
|
+
// Type guard for FigmaApiError
|
|
266
|
+
if (isFigmaApiError(error)) {
|
|
267
|
+
const status = error.statusCode
|
|
265
268
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
)
|
|
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
|
+
}
|
|
273
281
|
}
|
|
274
282
|
|
|
275
|
-
//
|
|
283
|
+
// Helper functions
|
|
284
|
+
const status = getErrorStatus(error) // number | null
|
|
285
|
+
const message = getErrorMessage(error) // string
|
|
286
|
+
|
|
287
|
+
// Convenience check
|
|
288
|
+
if (hasErrorStatus(error, 401)) {
|
|
289
|
+
// Handle unauthorized
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return <div>Error: {message}</div>
|
|
276
293
|
}
|
|
277
294
|
```
|
|
278
295
|
|
|
279
|
-
|
|
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
|
|
304
|
+
|
|
305
|
+
After mutations, use `useInvalidateVariables` to refresh cached data:
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import { useUpdateVariable, useInvalidateVariables } from '@figma-vars/hooks'
|
|
309
|
+
|
|
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()
|
|
322
|
+
|
|
323
|
+
// Option 2: Revalidate immediately (refetch now)
|
|
324
|
+
// revalidate()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<button
|
|
329
|
+
onClick={handleUpdate}
|
|
330
|
+
disabled={isLoading}>
|
|
331
|
+
{isLoading ? 'Updating...' : 'Update Variable'}
|
|
332
|
+
</button>
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
```
|
|
280
336
|
|
|
281
|
-
##
|
|
337
|
+
## โ๏ธ SWR Configuration
|
|
282
338
|
|
|
283
|
-
|
|
339
|
+
Customize SWR behavior globally through the provider:
|
|
284
340
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
+
```
|
|
289
360
|
|
|
290
|
-
|
|
361
|
+
**Common SWR Options:**
|
|
291
362
|
|
|
292
|
-
|
|
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
|
|
293
368
|
|
|
294
|
-
|
|
295
|
-
- `fileKey`: Figma file key for the target file. Required for API requests but can be `null` when using `fallbackFile`.
|
|
296
|
-
- `fallbackFile`: Optional local JSON file (as object or string) to use instead of API requests. Perfect for users without Figma Enterprise accounts.
|
|
369
|
+
## ๐ API Cheat Sheet
|
|
297
370
|
|
|
298
|
-
###
|
|
371
|
+
### Hooks
|
|
299
372
|
|
|
300
|
-
|
|
373
|
+
- **Queries**: `useVariables` (local), `usePublishedVariables` (library/published), `useVariableCollections`, `useVariableModes`, `useFigmaToken`
|
|
374
|
+
- **Mutations**: `useCreateVariable`, `useUpdateVariable`, `useDeleteVariable`, `useBulkUpdateVariables`
|
|
375
|
+
- **Cache**: `useInvalidateVariables` (invalidate/revalidate cache)
|
|
301
376
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
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
|
|
306
382
|
|
|
307
383
|
### Types
|
|
308
384
|
|
|
309
|
-
|
|
385
|
+
- **Responses**: `LocalVariablesResponse`, `PublishedVariablesResponse`
|
|
386
|
+
- **Variables**: `FigmaVariable`, `FigmaCollection`, `VariableMode`
|
|
387
|
+
- **Mutations**: `BulkUpdatePayload`, `CreateVariablePayload`, `UpdateVariablePayload`
|
|
388
|
+
- **Errors**: `FigmaApiError` (extends `Error` with `statusCode`)
|
|
310
389
|
|
|
311
|
-
|
|
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
|
|
312
397
|
|
|
313
|
-
|
|
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.
|
|
314
401
|
|
|
315
|
-
|
|
402
|
+
## ๐ซ Do Not Publish Tokens or File Keys
|
|
403
|
+
|
|
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.
|
|
408
|
+
|
|
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.
|
|
316
418
|
|
|
317
419
|
```tsx
|
|
318
|
-
//
|
|
319
|
-
import { FigmaVarsProvider
|
|
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
|
+
}
|
|
320
438
|
|
|
321
|
-
export
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
<TokenList />
|
|
326
|
-
</FigmaVarsProvider>
|
|
327
|
-
)
|
|
439
|
+
export default preview
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
- **Next.js App Router**: provide context in a shared provider file.
|
|
328
443
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
+
)
|
|
332
456
|
}
|
|
333
457
|
```
|
|
334
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
|
+
|
|
335
470
|
---
|
|
336
471
|
|
|
337
472
|
## ๐ Contributing
|
|
@@ -341,4 +476,4 @@ PRs and issues are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for gu
|
|
|
341
476
|
## ๐ License
|
|
342
477
|
|
|
343
478
|
This project is licensed under the [MIT License](LICENSE).
|
|
344
|
-
ยฉ 2024โ
|
|
479
|
+
ยฉ 2024โ2026 Mark Learst
|