Angular Integration

Add analytics to your Angular Figma plugin with the TodeService. This guide shows you how to track user actions in Angular-based Figma plugins and widgets.

Installation

Install the core SDK and Angular adapter:

Terminal
npm install @tode-sdk/core @tode-sdk/figma-angular

Setup

Initialize in Plugin Controller

Set up Tode in your plugin's main code file:

code.js
import { tode } from '@tode-sdk/core';

// Show your Angular UI
figma.showUI(__html__, { width: 400, height: 600 });

// Initialize Tode for Figma plugin analytics
await tode.init({ apiKey: 'your-api-key' });

// Optional: Track plugin open from controller
tode.trackAction('plugin_opened');

Configure Network Access

Add Tode's URLs to your plugin's manifest.json to enable analytics requests. Learn more about network access.

manifest.json
{
  "name": "Your Plugin",
  "id": "your-plugin-id",
  "api": "1.0.0",
  "main": "code.js",
  "ui": "index.html",
  "networkAccess": {
    "allowedDomains": [
      "https://mvp-events-processing-production.up.railway.app",
      "wss://mvp-events-processing-production.up.railway.app"
    ]
  }
}

Provide the Service in Your Angular App

Add TodeService to your app's providers:

app.config.ts
import { ApplicationConfig } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
 
export const appConfig: ApplicationConfig = {
  providers: [TodeService]
};

Use the Service in Your Components

Inject TodeService to track user actions:

app.component.js
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';

@Component({
selector: 'app-root',
template: `
  <button (click)="handleExport()" [disabled]="!isReady">
    Export Design
  </button>
  <p>Analytics: {{ isReady ? 'Connected' : 'Connecting...' }}</p>
`
})
export class AppComponent {
isReady = false;
subscription;

constructor(todeService) {
  this.todeService = todeService;
  this.subscription = this.todeService.isReady$.subscribe(
    (ready) => this.isReady = ready
  );
}

handleExport() {
  if (this.isReady) {
    this.todeService.trackAction('export_clicked');
  }
}

ngOnDestroy() {
  this.subscription.unsubscribe();
}
}

Service Reference

TodeService

Property/MethodTypeDescription
todeTodeThe Tode instance for direct access
isReady$Observable<boolean>Observable of ready state (tode.isReady)
trackAction(name)methodTrack a simple action (API)
trackActionWithMetric(name, value)methodTrack action with numeric value (API)
destroy()methodClean up resources

Examples

Basic Tracking

tool-button.component.js
import { Component, Input } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';

@Component({
selector: 'app-tool-button',
template: `<button (click)="handleClick()">{{ tool }}</button>`
})
export class ToolButtonComponent {
@Input() tool;
isReady = false;
subscription;

constructor(todeService) {
  this.todeService = todeService;
  this.subscription = this.todeService.isReady$.subscribe(
    (ready) => this.isReady = ready
  );
}

handleClick() {
  // Track which tool was selected in your Figma plugin
  if (this.isReady) {
    this.todeService.trackAction(`tool_selected_${this.tool}`);
  }
}

ngOnDestroy() {
  this.subscription.unsubscribe();
}
}

Track with Metrics

export-panel.component.js
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';

@Component({
selector: 'app-export-panel',
template: `
  <button (click)="handleExport()">
    Export {{ selectedItems.length }} Items
  </button>
`
})
export class ExportPanelComponent {
selectedItems = [];
isReady = false;
subscription;

constructor(todeService) {
  this.todeService = todeService;
  this.subscription = this.todeService.isReady$.subscribe(
    (ready) => this.isReady = ready
  );
}

async handleExport() {
  // Perform export logic...

  // Track export with item count for Figma widget analytics
  if (this.isReady) {
    this.todeService.trackActionWithMetric('items_exported', this.selectedItems.length);
  }
}

ngOnDestroy() {
  this.subscription.unsubscribe();
}
}

Track Feature Views

feature-view.component.js
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
import { filter, take } from 'rxjs';

@Component({
selector: 'app-feature-view',
template: `<div>Feature Content</div>`
})
export class FeatureViewComponent {
subscription;

constructor(todeService) {
  this.todeService = todeService;
}

ngOnInit() {
  // Track feature view once when Tode is ready
  this.subscription = this.todeService.isReady$.pipe(
    filter((ready) => ready),
    take(1)
  ).subscribe(() => {
    this.todeService.trackAction('feature_viewed');
  });
}

ngOnDestroy() {
  this.subscription?.unsubscribe();
}
}

Using AsyncPipe

async-example.component.ts
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
import { CommonModule, AsyncPipe } from '@angular/common';
 
@Component({
  selector: 'app-async-example',
  standalone: true,
  imports: [CommonModule, AsyncPipe],
  template: `
    <button
      (click)="track()"
      [disabled]="!(todeService.isReady$ | async)">
      Track Action
    </button>
    <p>Status: {{ (todeService.isReady$ | async) ? 'Ready' : 'Connecting...' }}</p>
  `
})
export class AsyncExampleComponent {
  constructor(public todeService: TodeService) {}
 
  track(): void {
    this.todeService.trackAction('button_clicked');
  }
}

Standalone Components

standalone.component.ts
import { Component, OnDestroy } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
import { Subscription } from 'rxjs';
 
@Component({
  selector: 'app-standalone',
  standalone: true,
  providers: [TodeService],
  template: `
    <button (click)="track()" [disabled]="!isReady">
      Track
    </button>
  `
})
export class StandaloneComponent implements OnDestroy {
  isReady = false;
  private subscription: Subscription;
 
  constructor(private todeService: TodeService) {
    this.subscription = this.todeService.isReady$.subscribe(
      ready => this.isReady = ready
    );
  }
 
  track(): void {
    if (this.isReady) {
      this.todeService.trackAction('standalone_action');
    }
  }
 
  ngOnDestroy(): void {
    this.subscription.unsubscribe();
    this.todeService.destroy();
  }
}

Best Practices

1. Always Unsubscribe

TypeScript
import { Subscription } from 'rxjs';
 
export class MyComponent implements OnDestroy {
  private subscription: Subscription;
 
  constructor(private todeService: TodeService) {
    this.subscription = this.todeService.isReady$.subscribe(...);
  }
 
  ngOnDestroy(): void {
    // Always clean up subscriptions
    this.subscription.unsubscribe();
  }
}

2. Use takeUntilDestroyed (Angular 16+)

TypeScript
import { Component, DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
 
@Component({...})
export class ModernComponent {
  private destroyRef = inject(DestroyRef);
  private todeService = inject(TodeService);
  isReady = false;
 
  constructor() {
    this.todeService.isReady$
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe(ready => this.isReady = ready);
  }
}

3. Use Descriptive Action Names

TypeScript
// Good - clear and descriptive
this.todeService.trackAction('export_to_png');
this.todeService.trackAction('layer_renamed');
 
// Bad - too vague
this.todeService.trackAction('click');
this.todeService.trackAction('action1');

4. Prefer AsyncPipe for Templates

TypeScript
// Good - automatic subscription management
template: `<button [disabled]="!(todeService.isReady$ | async)">`
 
// Also good but requires manual unsubscribe
isReady = false;
this.todeService.isReady$.subscribe(r => this.isReady = r);
template: `<button [disabled]="!isReady">`

Next Steps

Track your Figma plugin

Sign up to get your API key and start collecting analytics in minutes.

Get your API key