How to make your own widget

Guide explains how to create and add your own widget to Supervisely SDK

In this tutorial you will learn how to create your own widget, add it to Supervisely SDK and make small demo.

How to create your own widget

  1. Clone SKD repo:

    git clone
  2. Clone repo with widgets samples:

    git clone
  3. Create SDK folder symlink in ui-widgets-demos

    ln -s ./supervisely/supervisely ./ui-widgets-demos/supervisely
  4. Create folder in supervisely/app/widgets/<your_widget_folder> like that

    |____ template.html
    |____ script.js   # optional: you can add javascript file
  5. Declare a new class with inheritance from Widget in

    from import Widget
    from import StateJson, DataJson 
    class YourWidget(Widget):
        # ------ Required methods ------
        def __init__(
                data_1: list 
                data_2: dict 
            self._data_1 = data_1
            self._data_2 = data_2
            self._some_state_attribute_1 = True 
            self._some_state_attribute_2 = 42 
            super(YourWidget, self).__init__(widget_id=widget_id, file_path=__file__)
            # ------ Optional | Add your javascript file ------
            # script_path = "./sly/css/app/widgets/your_widget_folder/script.js" # change <your_widget_folder> 
            # JinjaWidgets().context["__widget_scripts__"][self.__class__.__name__] = script_path
        def get_json_data(self):
            """ This method will be used in template.html to get widget data """
            return {
                "data_1": self._data_1,
                "data_2": self._data_2,
        def get_json_state(self):
            """ This method will be used in template.html to get widget state """
            return {
                "some_state_attribute_1": self._some_state_attribute_1,
                "some_state_attribute_2": self._some_state_attribute_2,
        # ------ Optional methods | Just for example ------
        def your_method_for_change_data(self):
            # make changes in widget data
            self._data_1 = self._data_1 + [1, 2, 3, 4, 5]
            self._data_2 = {'a': 11}
            # save this changes of widget data in local storage(RAM) 
            DataJson()[self.widget_id]['data_1'] = self._data_1
            DataJson()[self.widget_id]['data_2'] = self._data_2
            # sync this changes to remote server 
        def your_method_for_change_state(self):
            # change state attribute
            self._some_state_attribute_1 = False
            self._some_state_attribute_2 = 0.1
            # save this changes of widget data in local storage(RAM) 
            StateJson()[self.widget_id]["some_state_attribute_1"] = self._some_state_attribute_1
            StateJson()[self.widget_id]["some_state_attribute_2"] = self._some_state_attribute_2
            # sync this changes to remote server 
  6. Construct template.html for your widget using other widgets from SDK or any HTML elements

        <!-- Elements from SDK had the "sly" prefix -->
        <sly-field :title="data.{{{widget.widget_id}}}.data_1" :description="state.{{{widget.widget_id}}}.some_state_attribute_1">
        <!-- Just simple HTML element with Javascript function from your scipt.js -->
        <!-- Also simple HTML element, but from UI library of HTML elements - -->
        <el-button :value="data.{{{widget.widget_id}}}.data_1"></el-button>
  7. (Optional) Prepare file script.js for your widget if you need to implement your own Vue JS component. 📗 See this example for more details.

    const myFunction = function (name) {
        console.log('Hello world')
  8. Import new widget as part of widgets module. Just add import in supervisely/app/widgets/

    # imports for other widgets as example
    from import RadioTabs
    from import TrainValSplits
    from import Editor
    # import for your widget
    from import YourWidgetClass
  9. Create folder with example of your widget in ui-widgets-demos/NEW widgets/<your_widget_example_folder> like that


    Example of

    import os
    import supervisely as sly
    from dotenv import load_dotenv
    from import (
        YourWidget #  <-- import your widget
    api = sly.Api()
    your_widget = YourWidget(data_1=[...], data_2={'c': 0.3355})
    card = Card(title="My card", content=your_widget)
    app = sly.Application(layout=card)
  10. To see the demo of your widget, you need to run the server. You got 2 options for that:

    a) Just run the server from command line

    python -m uvicorn "NEW widgets.grid_plot.src.main:app" --host --port 8000 --ws websockets --reload

    b) If you are VSCode user, then just edit ui-widgets-demos/.vscode/launch.json as showed below and run the server from "run & debug" menu

        "version": "0.2.0",
        "configurations": [
                "name": "Uvicorn",
                "type": "python",
                "request": "launch",
                "module": "uvicorn",
                "args": [
                    <!-- change "your_widget_example_folder" to the name of the folder with your widget -->
                    "NEW widgets.your_widget_example_folder.src.main:app",
                "jinja": true,
                "justMyCode": true,
                "env": {
                    "PYTHONPATH": "${workspaceFolder}:${PYTHONPATH}",
                    "LOG_LEVEL": "DEBUG",
  11. After all steps, test your widget in browser and if it ok make a commit & push & create the pull request in both repo(SDK and ui-widget-demos).

    Send the link to pull-request to Maxim Kolomeychenko(@Max) in Slack. He will run a new SDK build.


Simple widget example


from import DataJson, StateJson
from import Widget

class TextArea(Widget):
    def __init__(
        value: str = None,
        placeholder: str = "Please input",
        rows: int = 2,
        autosize: bool = True,
        readonly: bool = False,
        self._value = value
        self._placeholder = placeholder
        self._rows = rows
        self._autosize = autosize
        self._readonly = readonly
        super().__init__(widget_id=widget_id, file_path=__file__)

    def get_json_data(self):
        return {
            "value": self._value,
            "placeholder": self._placeholder,
            "rows": self._rows,
            "autosize": self._autosize,
            "readonly": self._readonly,

    def get_json_state(self):
        return {"value": self._value}

    def set_value(self, value):
        self._value = value
        StateJson()[self.widget_id]["value"] = value

    def get_value(self):
        return StateJson()[self.widget_id]["value"]

    def is_readonly(self):
        return DataJson()[self.widget_id]["readonly"]

    def enable_readonly(self):
        self._readonly = True
        DataJson()[self.widget_id]["readonly"] = True

    def disable_readonly(self):
        self._readonly = False
        DataJson()[self.widget_id]["readonly"] = False




from import TextArea

NEW widgets/textarea/src/

import supervisely as sly
from dotenv import load_dotenv
from import Card, TextArea


api = sly.Api()

text_area = TextArea(value="Some text in the text area.", autosize=False)
card = Card(title="My card", content=text_area)
app = sly.Application(layout=card)

Last updated