Below are the steps to get your widget running. You can also find instructions at:
https://www.figma.com/widget-docs/setup-guide/
This widget template uses TypeScript and NPM, two standard tools in creating JavaScript applications.
First, download Node.js which comes with NPM. This will allow you to install TypeScript and other libraries. You can find the download link here:
https://nodejs.org/en/download/
// code.js (or code.ts if using TypeScript)
// Single message handler for ALL message types figma.ui.onmessage = async (msg) => { console.log('Received message:', msg.type); // Debug log
if (msg.type === 'send-prompt') { try { const response = await sendToGemini(msg.prompt); figma.ui.postMessage({ type: 'ai-response', response: response }); } catch (error) { figma.ui.postMessage({ type: 'error', error: error.message }); } } else if (msg.type === 'set-api-key') { try { await figma.clientStorage.setAsync('apiKey', msg.key); console.log('API key saved successfully'); // Debug log figma.ui.postMessage({ type: 'key-saved' }); } catch (error) { console.error('Error saving API key:', error); figma.ui.postMessage({ type: 'error', error: 'Failed to save API key' }); } } else if (msg.type === 'check-api-key') { const apiKey = await figma.clientStorage.getAsync('apiKey'); console.log('API key exists:', !!apiKey); // Debug log figma.ui.postMessage({ type: 'key-status', hasKey: !!apiKey }); } };
// Function to call Gemini API async function sendToGemini(prompt) { // CRITICAL FIX: Must await the async call! const API_KEY = await figma.clientStorage.getAsync('apiKey');
console.log('API Key retrieved:', API_KEY ? 'Key exists (length: ' + API_KEY.length + ')' : 'No key found');
if (!API_KEY || API_KEY.trim() === '') { throw new Error('API key not set. Please set it in the plugin settings.'); }
const url = https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${API_KEY.trim()};
const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }], generationConfig: { temperature: 0.7, maxOutputTokens: 500 } }) });
if (!response.ok) {
const errorText = await response.text();
throw new Error(API error: ${response.statusText} - ${errorText});
}
const data = await response.json(); if (data.candidates && data.candidates[0] && data.candidates[0].content) { return data.candidates[0].content.parts[0].text; } else { throw new Error('No response from AI'); } }
// Show the UI when the plugin is launched figma.showUI(html, { width: 400, height: 500 }); Next, install TypeScript, esbuild and the latest type definitions by running:
npm install
If you are familiar with JavaScript, TypeScript will look very familiar. In fact, valid JavaScript code is already valid Typescript code.
TypeScript adds type annotations to variables. This allows code editors such as Visual Studio Code to provide information about the Figma API while you are writing code, as well as help catch bugs you previously didn't notice.
For more information, visit https://www.typescriptlang.org/
Using TypeScript requires a compiler to convert TypeScript (widget-src/code.tsx) into JavaScript (dist/code.js) for the browser to run. We use esbuild to do this for us.
We recommend writing TypeScript code using Visual Studio code:
- Download Visual Studio Code if you haven't already: https://code.visualstudio.com/.
- Open this directory in Visual Studio Code.
- Compile TypeScript to JavaScript: Run the "Terminal > Run Build Task..." menu item, then select "npm: watch". You will have to do this again every time you reopen Visual Studio Code.
That's it! Visual Studio Code will regenerate the JavaScript file every time you save.