Say you are trying to figure out whether the export feature you spent three weeks building is actually being used, or whether it is quietly ignored while everyone sticks to the same three menu items they have always used. You open the Figma plugin API docs looking for something resembling window.gtag or a simple pageview call, and you find nothing of the sort. The plugin sandbox does not give you a window object with network access at all, and that single fact reshapes almost everything about how tracking has to work here.
This guide walks through adding analytics tracking to a Figma plugin from the ground up, accounting for the sandbox restrictions from the start rather than discovering them halfway through implementation.
Step 1: Understand Why Standard Web Analytics Won’t Work
Figma plugins run in two separate contexts: the main sandboxed thread where your plugin logic executes, and an optional UI thread rendered in an iframe if your plugin has a visual interface. The main thread has no network access whatsoever — no fetch, no XMLHttpRequest, nothing that talks to the outside world. That restriction exists for good reason; it is a core part of how Figma keeps plugins from silently exfiltrating design data.
The iframe UI thread does have network access, since it behaves like a normal web page. That means any analytics call has to originate from the UI, not from the plugin’s main code, even if the event you want to track happens in the main thread. This forces a specific architecture: main thread detects the event, passes a message to the UI thread, and the UI thread sends the actual network request.
If you have built web analytics before, this is the first mental adjustment. There is no single script tag you drop in once and forget. Every trackable event needs a message-passing round trip before it can reach the outside world.
Step 2: Choose an Analytics Approach That Fits the Sandbox
Three broad options exist here, and each comes with real tradeoffs worth weighing before you commit.
Self-hosted lightweight endpoint. You stand up a minimal HTTP endpoint — a serverless function works fine — that accepts event data and logs it somewhere you control, whether that is a database, a spreadsheet via API, or a proper analytics store. This gives you full control over data handling and avoids third-party analytics scripts entirely, which matters given Figma’s review guidelines around what plugins are allowed to send externally.
Existing analytics service with an HTTP API. Services that support a plain HTTP measurement API (rather than requiring a browser-embedded SDK) can work from the plugin’s iframe, since you are just making a fetch call to their endpoint with your event payload. This saves you from building your own backend, at the cost of depending on a third party’s uptime and pricing.
Figma’s own plugin analytics (if your plugin is published on the Community). Figma provides basic usage analytics for published plugins directly in the plugin management dashboard — install counts, active users, that kind of aggregate data. It requires no code at all, but it is coarse. You get high-level numbers, not the granular event-level tracking most developers actually want, like which specific feature within the plugin got used.
For most plugins with more than a handful of features worth distinguishing, the self-hosted endpoint or an HTTP-API-based service is the practical choice. Figma’s built-in numbers are worth checking, but they will not answer the “is this specific feature being used” question on their own.
Step 3: Set Up Message Passing Between the Main Thread and the UI
With an approach chosen, the next piece is wiring up the message bridge itself. In your main plugin code, wherever an event worth tracking occurs — a button click handled in the main thread, a specific command being invoked, a successful export — post a message to the UI:
figma.ui.postMessage({
type: 'track-event',
eventName: 'export-completed',
properties: { format: 'svg', nodeCount: 12 }
});
In your UI code (the HTML/JS that runs in the iframe), listen for that message and forward it to your analytics endpoint:
window.onmessage = async (event) => {
const msg = event.data.pluginMessage;
if (msg.type === 'track-event') {
await fetch('https://your-analytics-endpoint.com/collect', {
method: 'POST',
body: JSON.stringify({
event: msg.eventName,
properties: msg.properties,
timestamp: Date.now()
})
});
}
};
If a plugin runs without a visible UI at all — some utility plugins execute a command and close immediately — you can spin up a hidden, zero-size iframe purely to serve as the network relay. It sounds like a workaround because it is one, but it is the standard pattern given the sandbox’s constraints.
Step 4: Decide What Events Are Worth Tracking
It is tempting to instrument everything once the plumbing exists, but that instinct produces noisy data that is harder to draw conclusions from, not easier. Start narrower.
Feature usage events tell you which parts of your plugin get exercised and which sit unused. If your plugin has five commands in its menu, tracking invocation counts for each one quickly reveals whether that fifth command was worth building.
Completion vs. abandonment events matter more than raw open counts. Tracking that a user opened your plugin’s export dialog is far less informative than tracking that they opened it and then closed it without exporting anything — the second event points directly at a friction problem worth investigating.
Error events deserve tracking on their own, separate from general usage. Knowing that a particular API call fails for 8% of users tells you something no amount of usage-count data will surface.
Resist the urge to track every click and hover. A handful of well-chosen events that map to real product questions will teach you more than a firehose of granular interaction data that nobody has time to sort through later.
Step 5: Handle User Privacy and Figma’s Review Guidelines
Figma’s plugin review process pays attention to what data plugins collect and where it goes, and analytics implementations are not exempt from that scrutiny. A few practical rules keep you clear of trouble.
Never send design content itself — layer names, text content, image data — to your analytics endpoint. Track that an export happened and what format was chosen; do not track what was inside the exported file. Anonymous or pseudonymous identifiers (a random ID generated once and stored via figma.clientStorage) are far safer than anything tied to a real user identity.
Disclose your data collection plainly in your plugin’s Community listing description. Users reading “this plugin collects anonymous usage analytics to improve feature development” know what they are agreeing to; users discovering undisclosed tracking after the fact tend to leave one-star reviews and file reports.
If your plugin serves teams under enterprise or organizational Figma accounts, some of those organizations restrict what network requests plugins are permitted to make at all. Building a graceful fallback — the plugin functions normally even if the analytics call silently fails — keeps a blocked tracking request from breaking core plugin functionality for those users.
Step 6: Test the Tracking Pipeline Before Trusting the Data
Before drawing any conclusions from real usage data, verify the pipeline end to end. Trigger each tracked event manually, confirm the message reaches the UI thread, confirm the network request fires, and confirm the event lands in your analytics store with the properties you expect attached to it.
A surprising number of tracking bugs come down to a mistyped eventName string or a message listener that was never actually attached in a particular UI state. Catching that during testing costs you twenty minutes. Catching it three months into production, after you have already drawn conclusions from data that was quietly incomplete the entire time, costs considerably more than that.
Testing across Figma’s desktop app and the browser version is worth doing too, since the iframe environment behaves slightly differently between them in ways that occasionally affect network requests.
A Quick Reference for Figma Plugin Analytics Implementation
| Step | What It Involves | Common Pitfall to Avoid |
|---|---|---|
| Choose an approach | Self-hosted endpoint, HTTP-API service, or Figma’s built-in analytics | Assuming a browser-based SDK will work in the main thread |
| Set up message passing | figma.ui.postMessage and a UI-side listener that forwards to your endpoint | Forgetting UI-less plugins need a hidden iframe relay |
| Select tracked events | Feature usage, completion/abandonment, errors | Instrumenting every click instead of meaningful events |
| Handle privacy | Anonymous IDs, no design content in payloads, clear disclosure | Sending layer or content data along with usage events |
| Test the pipeline | Manually trigger events and confirm data arrives correctly | Trusting data before verifying the tracking actually fires |
Once this pipeline is running, the export-feature question from the start of this post answers itself within a week or two of real usage — no guessing required, just numbers sitting in a dashboard telling you plainly whether that three-week feature earned its place in the menu.
Which part of this setup is giving you the most trouble — the message-passing bridge, choosing where to send the data, or figuring out which events are worth tracking in the first place? Describe where you’re stuck and I can help you work through the specific implementation.