Skip to content
Stand with Ukraine flag

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 four-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.
    • 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.

FieldTypeDescription
fqnstringFully 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
namestringHuman-readable widget name (e.g. Air quality index card)
descriptorobjectWidget configuration — must include a type field
descriptor.typestringOne of timeseries, latest, rpc, alarm, static
FieldTypeDescription
descriptionstringOne-sentence description (max 512 chars). Shown on browse cards
imagestringPreview image: data URI, /api/images/... reference (resolved from resources), external URL, or raw base64
tagsstring[]Free-form keywords used for search
deprecatedbooleanMark true to indicate the widget is deprecated
resourcesarrayEmbedded 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.

FieldDescription
typeWidget type (required)
sizeX, sizeYDefault size in grid units
templateHtmlAngular template HTML
templateCssCSS styles
controllerScriptJavaScript controller code
settingsFormForm schema for widget settings
dataKeySettingsFormForm schema for data key configuration
settingsDirectiveAngular directive for settings UI
dataKeySettingsDirectiveAngular directive for data key settings
defaultConfigJSON string of default widget configuration
hasBasicModeWhether widget supports basic configuration mode
basicModeDirectiveAngular directive for basic mode UI
resourcesEmbedded 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.

FieldRequiredSourceNotes
Nameyesname in JSONEditable
Imageyesimage + resources in JSONEditable — drop a new image to override
Descriptionyesdescription in JSONEditable, max 512 chars
CategoriesyesManualPick from Widget categories
Use CasesyesManualPick from Use cases
Supported ThingsBoard versionnoManualMin () and optional max (<) version
Professional EditionnoManualToggle if the widget requires PE
Tagsnotags in JSONEditable

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 dashboards.

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 400–800 px wide; the file itself should be well under 200 KB
  • Ensure the widget is legible at small card sizes

The Readme step of the upload wizard collects the long-form widget documentation — what the widget displays, data keys, configuration, value ranges — shown on your widget’s detail page. It is written in standard Markdown.

A good readme always includes:

  1. What the widget displays — one or two sentences
  2. Data keys — exactly which telemetry or attribute keys the widget expects, including their types
  3. Configuration — notable settings the user can adjust
  4. Value ranges — for color-coded widgets, document every range and its color

Example:

## Air quality index card
Displays the latest air quality index (AQI) telemetry in a scalable rectangle card.
### Features
- Color-coded value ranges (Good / Moderate / Unhealthy / Hazardous)
- Configurable icon with range-based coloring
- Auto-scaling layout
- Last update timestamp
### Data keys
- **air** (timeseries) — Air Quality Index value (0–500)
### AQI ranges
| Range | Level | Color |
|---------|--------------------|---------|
| 0–50 | Good | Green |
| 50–100 | Moderate | Yellow |
| 100–150 | Unhealthy (sensitive) | Orange |
| 150–200 | Unhealthy | Red |
| 200–300 | Very Unhealthy | Purple |
| 300+ | Hazardous | Maroon |

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:

HeadingWhat goes here
New featuresNew capabilities, configuration options, or behavior added in this version
Bug fixesDefects corrected since the previous version — describe the symptom users saw, not the internal cause
Breaking changesAnything that changes existing behavior in a way that can disrupt an installed copy — renamed keys, removed options, changed defaults
Migration notesThe 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.

StatusMeaning
Pending ReviewYour version is in the review queue and has not been evaluated yet
ApprovedYour version passed review and is now live on IoT Hub
RejectedYour 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