Skip to content

Cookie preferences

We use cookies for our own analytics, to see how our campaigns perform and, if you allow it, to load content from other services such as the Google site search. We never sell your data. Necessary cookies keep the site working and cannot be switched off. See our Cookie Policy for details and our Privacy Policy for how we handle personal data.

Security, load balancing and remembering your cookie choice.

Show us which pages people read and how they find the site, so we can improve it (Google Analytics).

Show us which of our ad campaigns bring visitors to the site and remember the campaign or partner link you arrived from (Google Ads, partner program). We do not use these cookies to build advertising profiles.

Load the site search from Google when you use it. Google sets its own cookies and shows ads in the search results.

© 2026 The ThingsBoard Authors
Try for free

ThingsBoard Cloud

Choose your data region

Your data stays in the region you choose, for residency and compliance. No credit card required.

Rather run it yourself? Install on your own servers

IoT Widget Contribution Guide

Welcome to ThingsBoard IoT Hub. This guide walks you through contributing a widget that end users can install on their ThingsBoard instance in a single click.

A widget contribution is a single JSON file that you export from ThingsBoard and upload through IoT Hub. When a user clicks Install on your widget, the platform imports it directly into their widget library where they can drop it onto any dashboard.


Create a free Creator account on the ThingsBoard Creator Portal to start publishing your items.


When a user installs your widget from IoT Hub:

  1. The platform downloads your widget JSON file.
  2. The widget is imported into the user’s Widget Library under a new widget bundle.
  3. The user can drop the widget onto any dashboard and configure data keys, targets, and styling as usual.

Your widget is self-contained — its HTML template, CSS, controller script, settings form, and default configuration all live inside the JSON. The end user does not need to install any dependencies or run any code outside ThingsBoard.

  1. In your ThingsBoard instance, build and test your widget in the Widget Library editor.
  2. Export it as a JSON file — see Export the widget.
  3. Go to IoT Hub → + Add new item. A five-step wizard walks you through the rest:
    • Upload — upload your exported JSON. The platform recognizes it as a widget automatically and pre-fills several fields.
    • Listing — fill in the listing metadata (see Fill in the listing). Name, Image, Description, and Tags are pre-filled from the JSON; Categories, Use Cases, and the supported ThingsBoard version must be selected manually.
    • Screenshots — optionally upload screenshots of the widget in use (see Screenshots).
    • Readme — write the long-form widget documentation in Markdown (see Readme content).
    • Review & Submit — verify everything and submit the version for review.

The sections below are a complete reference for each step.

  1. Open Resources → Widget Library in your ThingsBoard instance.
  2. Locate your widget in the list.
  3. In the row’s action cell, click the Export widget button.
  4. Save the resulting .json file — this is your upload file.

The exported file is a complete widget type definition and requires no further editing to be valid. If you want to override auto-derived fields (description, image, tags), you can edit the JSON directly before uploading.

A valid widget file is a JSON object with the following structure:

{
"fqn": "john_doe.air_quality_card",
"name": "Air quality index card",
"description": "Displays the latest air quality index telemetry in a scalable rectangle card.",
"image": "tb-image;/api/images/system/air_quality_index_card_system_widget_image.png",
"tags": ["weather", "environment", "air", "aqi"],
"deprecated": false,
"descriptor": {
"type": "latest",
"sizeX": 3,
"sizeY": 3,
"templateHtml": "...",
"templateCss": "...",
"controllerScript": "...",
"settingsForm": [],
"dataKeySettingsForm": [],
"defaultConfig": "{...}",
"resources": []
},
"resources": [
{
"link": "/api/images/system/air_quality_index_card_system_widget_image.png",
"title": "Air quality index card system widget image",
"type": "IMAGE",
"mediaType": "image/png",
"fileName": "air_quality_index_card_system_widget_image.png",
"data": "iVBORw0KGgoAAAANS..."
}
]
}

Every widget published to IoT Hub must use a namespaced fqn in the form nickname.widget_fqn, where nickname is your own identifier — a personal handle (e.g. john_doe) or a company name (e.g. thingsboard). This keeps your widget unique across the marketplace and prevents widget name collisions with other contributors’ widgets.

The ThingsBoard widget editor does not allow setting a custom namespace prefix in the UI — the exported JSON will contain only the plain widget name (e.g. air_quality_card). After exporting, open the .json file and prepend your nickname:

{
"fqn": "john_doe.air_quality_card",
...
}

Pick one nickname and reuse it for every widget you publish.

Field Type Description
fqn string Fully qualified name — a unique identifier across the widget library, in the form nickname.widget_fqn (e.g. john_doe.air_quality_card or acme.air_quality_card). Lowercase and underscores recommended. See Set the widget FQN
name string Human-readable widget name (e.g. Air quality index card)
descriptor object Widget configuration — must include a type field
descriptor.type string One of timeseries, latest, rpc, alarm, static
Field Type Description
description string One-sentence description (max 512 chars). Shown on browse cards
image string Preview image: data URI, /api/images/... reference (resolved from resources), external URL, or raw base64
tags string[] Free-form keywords used for search
deprecated boolean Mark true to indicate the widget is deprecated
resources array Embedded resources — images, external CDN scripts (e.g. ECharts), or ThingsBoard extensions. Populated automatically by ThingsBoard export

The descriptor object holds the widget’s visual and runtime definition. ThingsBoard populates all of these during export — you typically don’t need to hand-edit them.

Field Description
type Widget type (required)
sizeX, sizeY Default size in grid units
templateHtml Angular template HTML
templateCss CSS styles
controllerScript JavaScript controller code
settingsForm Form schema for widget settings
dataKeySettingsForm Form schema for data key configuration
settingsDirective Angular directive for settings UI
dataKeySettingsDirective Angular directive for data key settings
defaultConfig JSON string of default widget configuration
hasBasicMode Whether widget supports basic configuration mode
basicModeDirective Angular directive for basic mode UI
resources Embedded resources (images, external scripts)

The Listing step of the upload wizard collects the fields shown on the browse card and detail page. Name, Image, Description, and Tags are autofilled from your JSON; everything else you set here. Version and changelog are set per upload — see Versioning and checksum.

Field Required Source Notes
Name yes name in JSON Editable
Image yes image + resources in JSON Editable — drop a new image to override
Description yes description in JSON Editable, max 512 chars
Categories yes Manual Pick from Widget categories
Use Cases yes Manual Pick from Use cases
Supported ThingsBoard version no Manual Min (≥) and optional max (<) version
Professional Edition no Manual Toggle if the widget uses features that were PE-only before ThingsBoard 4.4; the listing then shows a PE or 4.4+ badge
Tags no tags in JSON Editable

Pick one or more categories that describe what your widget is. Allowed values: Cards & Info, Charts & Graphs, Controls, Gauges & Indicators, Input Forms, Maps & Location, SCADA, Tables & Lists.

Pick one or more IoT domains where your widget is useful. These are shared across the whole marketplace so users browsing by use case will see your widget alongside matching devices and solution templates.

Allowed values: Air Quality Monitoring, Asset Tracking, Cold Chain, Drones, Environment Monitoring, Fleet Tracking, Health Care, Industrial Automation, Predictive Maintenance, Robotics, SCADA, Smart Building, Smart City, Smart Energy, Smart Farming, Smart Home, Smart Metering, Smart Office, Smart Retail, Solar Monitoring, Tank Level Monitoring, Waste Management.

The preview image is shown on browse cards and the widget’s detail page. A good preview image is critical — it’s the single biggest driver of whether users click on your widget.

There are two ways to set it:

  • From the widget JSON — exports from ThingsBoard already embed a preview image, so it works out of the box.
  • From the upload UI — in the Listing step of the wizard, drop a new file to override whatever came from the JSON.

Best practices.

  • Use PNG for clean screenshots and diagrams
  • Capture the widget in a realistic state (with actual data, not placeholder values)
  • Crop tightly to the widget — avoid surrounding dashboard chrome
  • Target at least 1200 px wide and keep the file under 500 KB — the preview is also what opens when a user clicks it on the detail page, so it has to hold up at full screen
  • Ensure the widget is legible at small card sizes

Screenshots are a separate set of images from the preview image — the preview is the single card image, screenshots are the gallery on your widget’s detail page. They are optional, but a widget with screenshots is much easier to evaluate: users see the widget in more than one state before they install it.

Upload them in the Screenshots step of the new-item and new-version wizards, or later from the Screenshots tab of the version drawer. The gallery leads with the preview image and then follows your screenshots in the order you arrange them, so the picture users clicked in from is still the first thing they see. Any image in it opens full screen when clicked.

Requirements.

  • At least 1280 px wide; 1600×900 is recommended
  • 16:9 is the aspect ratio the gallery frame is built for — other ratios are letterboxed, not cropped
  • PNG or JPG
  • Up to 2 MB per file
  • Up to 8 screenshots per listing

Best practices.

  • Order them so the one that best explains the widget comes first — it is the slide right after the preview image
  • Show the widget with realistic data, and use the remaining slots for other states or settings worth seeing
  • Crop to the widget and its immediate context, not the whole dashboard

The Readme step of the upload wizard collects the long-form widget documentation shown on your widget’s detail page. It is written in standard Markdown.

A good readme has five sections:

  1. Who it’s for — one paragraph naming the persona and the question they are trying to answer. Users scan this to decide whether the widget fits their use case before reading anything else.
  2. What it does — a short prose sentence describing the widget’s core visual behavior, followed by a bullet list of notable features (layout, indicators, optional elements, limitations).
  3. How to set up — a ### Data keys table with columns Key, Role, Type (Timeseries / Attribute), and Description (units, expected range). Follow the table with any note about edge-case behavior (e.g. what renders when a key has never reported).
  4. How to customize — a bullet list of the settings users are most likely to change, each written as **To do X** — do Y.
  5. Tips (optional) — one or two non-obvious tips that help users get the most value from the widget.

Example:

## Who it's for
Dashboard authors who need a temperature to be readable from across a room ask "is
this zone running hot or cold right now?" They are usually looking at a wall
display or a shared floor-plan dashboard, where nobody is going to hover over a
chart to read a tooltip, and where the answer has to be right at a glance.
## What it does
A cold-room sensor reporting `4.2` shows a blue thermometer and a blue value; the
same card reads amber at `8.0` and red at `12.0`, so the zone is obvious before the
number is.
- Auto-scaling layout that fits any card size
- Icon and value color follow the same temperature bands, so they always agree
- Optional last-updated timestamp
- Configurable label, icon, and background
- Shows one temperature at a time; it does not chart history or compare zones
## How to set up
### Data keys
| Key | Role | Type | Description |
|---|---|---|---|
| Temperature key (user-configured) | Main value | Timeseries | Numeric reading, °C or °F depending on **Units** |
The card needs one numeric timeseries key. If the key has never reported, the card
renders its label and icon with an empty value rather than an error, so an offline
device looks empty rather than broken.
## How to customize
- **To use your own temperature bands** — edit the **Icon color ranges** and
**Value color ranges** lists; each entry sets a `to` boundary and a color.
- **To switch °C/°F** — change **Units**.
- **To hide the label, icon, or last-updated time** — turn off **Show label**,
**Show icon**, or **Show date**.
- **To reposition the label** — change **Label position** to top or bottom.
- **To resize the icon** — change **Icon size**.
- **To match your dashboard's font** — set **Value font** and **Label font**.
## Tips
- Keep **Icon color ranges** and **Value color ranges** on the same boundaries, so
the icon and the number always agree on the zone.

Each widget version is identified by a semver string (e.g. 1.0.0) plus a changelog entry, bumped every time you upload a changed JSON, and an SHA-256 checksum computed from:

  • fqn
  • name
  • descriptor (the entire descriptor object as JSON)

This means:

  • Changing HTML, CSS, JS, or any descriptor field → checksum changes → you must bump the version
  • Changing only description, image, or tags → checksum does not change (metadata-only update)
  • Same version + different checksum = the system detects a mismatch and rejects the update

Always bump the version when uploading a changed widget JSON.

When you upload a new version, pair the version bump with a changelog entry — a short note that tells users exactly what changed since the previous version, so they can decide whether, and how, to upgrade.

Summarize what changed since the previous version: new features, bug fixes, breaking changes, and any migration notes users need to know.

Group your entry under these headings, and include only the ones that apply:

Heading What goes here
New features New capabilities, configuration options, or behavior added in this version
Bug fixes Defects corrected since the previous version — describe the symptom users saw, not the internal cause
Breaking changes Anything that changes existing behavior in a way that can disrupt an installed copy — renamed keys, removed options, changed defaults
Migration notes The concrete steps an existing user must take to move from the previous version to this one
  • Lead with the user impact. Describe what the user can now do, or what no longer breaks — not how you implemented it.
  • Be specific. Name the exact keys, fields, settings, or outputs that changed. “Renamed the output key from temp to temperature” is actionable; “improved naming” is not.
  • One change per bullet. Keep each item to a single, scannable line.
  • Flag breaking changes loudly. If an upgrade can disrupt an installed copy, say so explicitly and pair it with a migration note.

Pair every entry with a semantic version bump — patch (1.0.1) for fixes, minor (1.1.0) for backward-compatible features, major (2.0.0) for breaking changes.

## 2.0.0
### Breaking changes
- Renamed the output key from `temp` to `temperature` to match the
ThingsBoard telemetry convention.
### Migration notes
- Update any dashboards, alarm rules, or downstream calculated fields
that read the `temp` key to read `temperature` instead.
### New features
- Added an optional `humidity` argument; when present, the formula
now also computes a `dewPoint` output.
### Bug fixes
- Fixed missing output when the input telemetry arrived as a string
instead of a number.

Once you complete the upload wizard and click Submit, your version enters the IoT Hub review queue. The ThingsBoard team checks every submission before it goes live.

To see the current status of your submission, open the Creator Portal and go to Items. Find your item in the list and click the Manage Versions icon in its row. The Versions page lists every version you have uploaded with a Status column that updates in real time.

Status Meaning
Pending Review Your version is in the review queue and has not been evaluated yet
Published Your version passed review and is now live on IoT Hub
Rejected Your version did not pass review — see the reviewer comment for details

Reviewers verify that the submission meets the same criteria as the Pre-Upload Checklist: the widget JSON is valid and imports cleanly, the FQN is correctly namespaced, and the listing and readme give users enough context to evaluate and use the widget.

The Status column will show Rejected. Open the version details to read the reviewer’s comment, which explains specifically what needs to be fixed.

To resubmit:

  1. Fix the reported issues in your local package.
  2. Return to Items → Manage Versions for your item.
  3. Click + Upload new version and complete the wizard with the corrected package.
  • Widgets — how end users discover and install a widget