Skip to content
Stand with Ukraine flag

Widget Development

Custom widgets let you build tailored UI components — cards, charts, control panels, tables — that plug directly into ThingsBoard dashboards. You write HTML, CSS, and JavaScript in the built-in Widget Editor; no external build tools or IDE required.

Every widget lives inside a widget bundle (a folder-like container in the Widget Library). ThingsBoard ships with dozens of bundles for charts, maps, controls, and more. When the built-in widgets don’t fit your use case, you create a custom widget type inside your own bundle.

TypeData sourceTypical use
Latest ValuesLast known value of each data keySensor cards, gauges, status indicators
Time-SeriesHistorical data within a time windowCharts, trend lines, heatmaps
RPC (Control)Sends RPC commands, displays responsesButtons, sliders, device control panels
AlarmActive alarms matching filter criteriaAlarm tables, severity dashboards
StaticNo data subscriptionNavigation menus, info banners, branding

Open Widget Library from the left menu, then click any widget type to open the Widget Editor. The editor has the following sections:

Toolbar — Save, Undo, Redo, and Run buttons. Click Save (or Ctrl+S) to save your widget. Click Run (or Ctrl+Enter) to preview your widget without saving.

Resources tab — Add external JavaScript or CSS libraries by URL (e.g., ECharts, D3). They’re loaded before your code executes.

HTML tab — Your widget’s Angular template. Supports Angular directives (*ngIf, *ngFor, [ngStyle], [ngClass]), Angular Material components (mat-icon, mat-button, mat-select), and ThingsBoard components (tb-icon, tb-time-series-chart-widget).

CSS tab — Scoped styles for your widget. Use standard CSS — it’s automatically scoped to your widget instance.

JavaScript tab — Widget logic via lifecycle functions:

  • self.onInit() — called once when the widget loads
  • self.onDataUpdated() — called each time new data arrives
  • self.onResize() — called when the widget container resizes
  • self.onDestroy() — called when the widget is removed
  • self.onEditModeChanged() — called when the dashboard switches between view and edit mode
  • self.onMobileModeChanged() — called when the dashboard enters or exits mobile mode
  • self.typeParameters() — returns configuration like max datasources, data keys, etc.
  • self.actionSources() — declares clickable actions the widget exposes

Inside every lifecycle function, self.ctx is your main entry point to the platform — it provides access to settings, data, the DOM container, HTTP client, and platform services. See the Widget context (ctx) reference for the full list of available properties.

Settings form tab — Defines the configuration form end users see in the widget settings dialog. Fields are added through a built-in form — click the add button, select a field type, and fill in the field properties. Each field has an id, display name, type, and optional group, default, and condition. See the Field types reference for the full list of supported field types.

Data key settings form — Defines the configuration form shown in the data key settings dialog. Each data key already has built-in options (label, color); the data key settings form lets you add extra fields specific to your widget — for example, a threshold value or display format per key.

Widget settings — Stores basic metadata: widget image, description, deprecated status, and the selectors that link to the settings form and data key settings form.

Preview section — Shows a live preview of your widget. Configure test datasources and time windows to simulate real data.

Build a minimal static widget that displays a configurable greeting message.

Hello World widget preview showing the greeting message
  1. Navigate to Widget Library → click + (Create new widget bundle) → name it “My Widgets” → save.

  2. Open the bundle → click + (Create new widget type) → select Static as the type.

  3. In the HTML tab, paste:

    <div class="hello-container">
    <div class="hello-icon">
    <mat-icon>waving_hand</mat-icon>
    </div>
    <div class="hello-text" id="greeting"></div>
    </div>
  4. In the CSS tab, paste:

    .hello-container {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    }
    .hello-icon mat-icon {
    font-size: 36px;
    width: 36px;
    height: 36px;
    color: var(--tb-primary-500);
    }
    .hello-text {
    font-size: 24px;
    font-weight: 600;
    color: #344054;
    }
  5. In the JavaScript tab, paste:

    self.onInit = function() {
    let settings = self.ctx.settings || {};
    let message = settings.greetingMessage || 'Hello, ThingsBoard!';
    let element = self.ctx.$container[0].querySelector('#greeting');
    if (element) {
    element.textContent = message;
    }
    };
    self.typeParameters = function() {
    return {
    datasourcesOptional: true,
    dataKeysOptional: true
    };
    };

    This reads the greetingMessage from widget settings and writes it into the DOM. The typeParameters function tells ThingsBoard that this widget doesn’t require a datasource.

  6. In the Settings form tab, add a field using one of two methods:

    • Import from JSON — click the Import from JSON button, select the JSON content tab, and paste:

      [
      {
      "id": "greetingMessage",
      "name": "Greeting message",
      "group": "Appearance",
      "type": "text",
      "default": "Hello, ThingsBoard!",
      "required": false
      }
      ]
    • Manually — click the add button, select text as the field type, and fill in the id, name, group, and default properties in the form.

    This creates a text field in the widget settings dialog where users can customize the greeting.

  7. Click Run to preview — you should see “Hello, ThingsBoard!” centered in the preview panel.

  8. Click Save (or Ctrl+S) → name the widget “Hello World”.

  9. Open any dashboard → click Add widget → find your “My Widgets” bundle → add the “Hello World” widget. Open its settings to change the greeting message and verify it updates.

The Hello World widget demonstrates the core pattern every widget follows:

  • HTML defines the structure — static markup or Angular templates.
  • CSS styles the widget — scoped to the widget container.
  • JavaScript provides behavior via lifecycle functions. self.onInit() runs once to set up the widget; self.ctx gives you access to settings, data, services, and the DOM container.
  • Settings form defines the per-widget configuration form. Data key settings form defines a parallel form shown per data key, where you can add extra fields beyond the built-in label and color options. Each field in both forms has an id, display name, type, and optionally a group, default, and condition.
  • Type parameters tell the platform what data configuration the widget expects (or doesn’t expect).

Each widget type has its own page with an overview, use cases, and two hands-on examples: