I build a lot of Figma plugins. But when you start working on larger plugins, you quickly realize the need to store data—things like user settings and preferences—so they persist between sessions.
At first, I wasn’t sure how to handle this. Every time the plugin restarted, everything reset, which wasn’t ideal. That’s when I discovered Figma’s clientStorage
API. It seemed like the perfect solution, but using it efficiently required some trial and error.
After a lot of experimenting and trial and error, I built a more flexible and efficient storage solution. Now, managing plugin data feels effortless. Let me walk you through the basics of clientStorage and how you can use it to create better, more powerful Figma plugins.
How it started
// Store Data
await figma.clientStorage.setAsync('my_color', '#FF0000');
// Retrieve it later
const savedColor = await figma.clientStorage.getAsync('my_color');
console.log(savedColor); // '#FF0000'
This simple key-value storage system is the foundation of data persistence in Figma plugins. The setAsync
method takes a string key and any JSON-serializable value, while getAsync
retrieves data using that same key.
Then I wrapped it inside a fuction like:
const PLUGIN_KEY = 'example_plugin';
class Storage {
static async save(data) {
await figma.clientStorage.setAsync(PLUGIN_KEY, data);
}
static async load() {
return await figma.clientStorage.getAsync(PLUGIN_KEY);
}
}
I use const PLUGIN_KEY = 'example_plugin'
as a way to create a
The clientStorage API is shared across all Figma plugins. Using a unique identifier as a prefix ensures my plugin's data won't collide with data from other plugins.
Adding Type saftey and Error handling
I added proper error handling to prevent plugin crashes:
export class ClientStorage {
// Save data
static async save(key: string, data: any): Promise<boolean> {
try {
await figma.clientStorage.setAsync(key, data);
return true;
} catch (error) {
console.error(`[Storage] Save error (${key}):`, error);
return false;
}
}
// Get data
static async get(key: string): Promise<any> {
try {
return await figma.clientStorage.getAsync(key);
} catch (error) {
console.error(`[Storage] Get error (${key}):`, error);
return null;
}
}
// Delete data
static async delete(key: string): Promise<boolean> {
try {
await figma.clientStorage.deleteAsync(key);
return true;
} catch (error) {
console.error(`[Storage] Delete error (${key}):`, error);
return false;
}
}
}
Usages
// Save user preferences
await ClientStorage.save('userPrefs', { theme: 'dark', autoSave: true });
// Retrieve user preferences
const prefs = await ClientStorage.get('userPrefs');
if (prefs) {
applyUserSettings(prefs);
}
// Clear user data on logout
await ClientStorage.delete('userPrefs');
Adding update method
Added an update method that merges new data with existing data instead of replacing it completely:
// Update existing data (merge)
static async update(key: string, newData: any): Promise<boolean> {
try {
const existing = await this.get(key) || {};
const updated = { ...existing, ...newData };
return await this.save(key, updated);
} catch (error) {
console.error(`[Storage] Update error (${key}):`, error);
return false;
}
}
This was a game-changer for updating specific properties without touching others:
await ClientStorage.update('userPrefs', { theme: 'light' });
Final Example
// Store colors
await ClientStorage.save('colors', { primary: '#3498db', secondary: '#2ecc71' });
// Store typography settings
await ClientStorage.save('typography', { fontFamily: 'Inter', baseSize: 16 });
// Load each when needed
const colors = await ClientStorage.get('colors');
const typography = await ClientStorage.get('typography');
Reflecting on the Journey
What started as a simple storage need evolved into a robust system that made my plugin more reliable and my code more maintainable. The key insights were:
- Start with the simplest approach that works
- Look for patterns in your code that can be abstracted
- Handle errors gracefully to prevent plugin crashes
- Build convenience methods that match your usage patterns
By investing a little time in creating this storage wrapper, I saved hours of debugging and made future development much easier. Whether you're building your first Figma plugin or your tenth, I hope this approach helps you create more robust storage solutions for your own projects!
WIP mock of Plugin I'm working on

stay hungry, stay foolish
-Steve Jobs
©realvjy✦vijay verma