@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.
- package/README.md +371 -177
- package/dist/api/fetcher.d.ts.map +1 -1
- package/dist/api/index.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/FigmaTokenContext.d.ts.map +1 -1
- package/dist/contexts/FigmaVarsProvider.d.ts +1 -1
- package/dist/contexts/FigmaVarsProvider.d.ts.map +1 -1
- package/dist/contexts/index.d.ts +1 -1
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/contexts/useFigmaTokenContext.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/useFigmaToken.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/useVariableCollections.d.ts.map +1 -1
- package/dist/hooks/useVariableModes.d.ts.map +1 -1
- package/dist/hooks/useVariables.d.ts +3 -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 +235 -162
- package/dist/types/contexts.d.ts +29 -3
- package/dist/types/contexts.d.ts.map +1 -1
- package/dist/types/figma.d.ts +56 -3
- package/dist/types/figma.d.ts.map +1 -1
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/mutations.d.ts +4 -4
- 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/filterVariables.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +75 -31
- package/scripts/export-variables.mjs +101 -0
- 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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
+
**Example Output:**
|
|
62
83
|
|
|
63
|
-
|
|
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
|
-
|
|
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-
|
|
97
|
+
const FIGMA_FILE_KEY = 'your-file-key'
|
|
75
98
|
|
|
76
|
-
|
|
77
|
-
|
|
99
|
+
function App() {
|
|
100
|
+
return (
|
|
78
101
|
<FigmaVarsProvider
|
|
79
102
|
token={FIGMA_TOKEN}
|
|
80
|
-
fileKey={FIGMA_FILE_KEY}
|
|
81
|
-
|
|
103
|
+
fileKey={FIGMA_FILE_KEY}
|
|
104
|
+
swrConfig={{
|
|
105
|
+
revalidateOnFocus: false,
|
|
106
|
+
dedupingInterval: 5000,
|
|
107
|
+
}}>
|
|
108
|
+
<Tokens />
|
|
82
109
|
</FigmaVarsProvider>
|
|
83
|
-
|
|
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
|
-
|
|
122
|
+
## ๐งฉ Non-SWR Usage (Core entrypoint)
|
|
88
123
|
|
|
89
|
-
|
|
124
|
+
Use the `/core` build when you prefer Axios/TanStack/server scripts without React/SWR.
|
|
90
125
|
|
|
91
|
-
|
|
92
|
-
// src/components/TokenList.tsx
|
|
93
|
-
import { useVariables } from '@figma-vars/hooks'
|
|
126
|
+
**Axios example (GET + bulk PUT)**
|
|
94
127
|
|
|
95
|
-
|
|
96
|
-
|
|
128
|
+
```ts
|
|
129
|
+
import axios from 'axios'
|
|
130
|
+
import { FIGMA_FILE_VARIABLES_PATH } from '@figma-vars/hooks/core'
|
|
97
131
|
|
|
98
|
-
|
|
99
|
-
|
|
132
|
+
const token = process.env.FIGMA_TOKEN!
|
|
133
|
+
const fileKey = process.env.FIGMA_FILE_KEY!
|
|
100
134
|
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const {
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
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
|
-
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
255
|
+
## ๐ก๏ธ Error Handling
|
|
166
256
|
|
|
167
|
-
|
|
257
|
+
3.0.0 introduces powerful error handling utilities for type-safe error checking:
|
|
168
258
|
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
262
|
+
function ErrorHandler({ error }: { error: Error | null }) {
|
|
263
|
+
if (!error) return null
|
|
175
264
|
|
|
176
|
-
|
|
265
|
+
// Type guard for FigmaApiError
|
|
266
|
+
if (isFigmaApiError(error)) {
|
|
267
|
+
const status = error.statusCode
|
|
177
268
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
197
|
-
|
|
287
|
+
// Convenience check
|
|
288
|
+
if (hasErrorStatus(error, 401)) {
|
|
289
|
+
// Handle unauthorized
|
|
198
290
|
}
|
|
199
291
|
|
|
200
|
-
return <
|
|
292
|
+
return <div>Error: {message}</div>
|
|
201
293
|
}
|
|
202
294
|
```
|
|
203
295
|
|
|
204
|
-
|
|
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
|
-
|
|
305
|
+
After mutations, use `useInvalidateVariables` to refresh cached data:
|
|
207
306
|
|
|
208
307
|
```tsx
|
|
209
|
-
|
|
210
|
-
import { useVariables } from '@figma-vars/hooks'
|
|
308
|
+
import { useUpdateVariable, useInvalidateVariables } from '@figma-vars/hooks'
|
|
211
309
|
|
|
212
|
-
function
|
|
213
|
-
const {
|
|
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
|
-
|
|
216
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
361
|
+
**Common SWR Options:**
|
|
233
362
|
|
|
234
|
-
- `
|
|
235
|
-
- `
|
|
236
|
-
- `
|
|
237
|
-
- `
|
|
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
|
-
|
|
369
|
+
## ๐ API Cheat Sheet
|
|
240
370
|
|
|
241
|
-
|
|
371
|
+
### Hooks
|
|
242
372
|
|
|
243
|
-
- `
|
|
244
|
-
-
|
|
245
|
-
-
|
|
246
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
260
|
-
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
|
+
}
|
|
261
438
|
|
|
262
|
-
export
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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โ
|
|
479
|
+
ยฉ 2024โ2026 Mark Learst
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/api/fetcher.ts"],"names":[],"mappings":"
|
|
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"}
|