What is IDOM?

IDOM is a Python web framework for building interactive websites without needing a single line of Javascript. This is accomplished by breaking down complex applications into nestable and reusable chunks of code called “components” that allow you to focus on what your application does rather than how it does it.

Ecosystem independence is also a core feature of IDOM. It can be added to existing applications built on a variety of sync and async web servers, as well as integrated with other frameworks like Django, Jupyter, and Plotly Dash. Not only does this mean you’re free to choose what technology stack to run on, but on top of that, you can run the exact same components wherever you need them. For example, you can take a component originally developed in a Jupyter Notebook like this one, and embed it in your production application without changing anything about the component itself.

Getting Started

The first thing we need must do in order to run IDOM in a Jupyter Notebook is import idom_jupyter. After that, you'll be able to use IDOM components like any other Jupyter Widget.

In [ ]:
import idom_jupyter

At a Glance

To get a rough idea of how to write apps in IDOM, take a look at the tiny “hello world” application below:

In [ ]:
from idom import component, html, run


@component
def App():
    return html.h1("Hello, World!")


App()

Creating Interfaces

IDOM is a Python package for making user interfaces (UI). These interfaces are built from small elements of functionality like buttons text and images. IDOM allows you to combine these elements into reusable “components”. Once you learn how these UI elements are created and organized into components you'll be able to do things like create interfaces from raw data:

In [ ]:
from idom import component, html


@component
def DataList(items, filter_by_priority=None, sort_by_priority=False):
    if filter_by_priority is not None:
        items = [i for i in items if i["priority"] <= filter_by_priority]
    if sort_by_priority:
        items = list(sorted(items, key=lambda i: i["priority"]))
    list_item_elements = [html.li(i["text"], key=i["id"]) for i in items]
    return html.ul(list_item_elements)


@component
def TodoList():
    tasks = [
        {"id": 0, "text": "Make breakfast", "priority": 0},
        {"id": 1, "text": "Feed the dog", "priority": 0},
        {"id": 2, "text": "Do laundry", "priority": 2},
        {"id": 3, "text": "Go on a run", "priority": 1},
        {"id": 4, "text": "Clean the house", "priority": 2},
        {"id": 5, "text": "Go to the grocery store", "priority": 2},
        {"id": 6, "text": "Do some coding", "priority": 1},
        {"id": 7, "text": "Read a book", "priority": 1},
    ]
    return html.section(
        html.h1("My Todo List"),
        DataList(tasks, filter_by_priority=1, sort_by_priority=True),
    )


TodoList()

Adding Interactivity

Components often need to change what’s on the screen as a result of an interaction. For example, typing into the form should update the input field, clicking a “Comment” button should bring up a text input field, clicking “Buy” should put a product in the shopping cart. Components need to “remember” things like the current input value, the current image, the shopping cart. In IDOM, this kind of component-specific memory is created and updated with a “hook” called use_state() that creates a state variable and state setter respectively:

In [ ]:
import json
from pathlib import Path

from idom import component, use_state, html


DATA_PATH = Path().parent / "data" / "gallery-data.json"
sculpture_data = json.loads(DATA_PATH.read_text())


@component
def Gallery():
    index, set_index = use_state(0)

    def handle_click(event):
        set_index(index + 1)

    bounded_index = index % len(sculpture_data)
    sculpture = sculpture_data[bounded_index]
    alt = sculpture["alt"]
    artist = sculpture["artist"]
    description = sculpture["description"]
    name = sculpture["name"]
    url = sculpture["url"]

    return html.div(
        html.button({"onClick": handle_click}, "Next"),
        html.h2(name, " by ", artist),
        html.p(f"({bounded_index + 1} or {len(sculpture_data)})"),
        html.img({"src": url, "alt": alt, "style": {"height": "200px"}}),
        html.p(description),
    )


Gallery()

Javascript Integration

While IDOM is a great tool for displaying HTML and responding to browser events with pure Python, there are other projects which already allow you to do this inside Jupyter Notebooks or in standard web apps. The real power of IDOM comes from its ability to seamlessly leverage the existing Javascript ecosystem:

In [ ]:
from idom import component, web


victory = web.module_from_template("react", "victory-bar", fallback="⌛")
VictoryBar = web.export(victory, "VictoryBar")


@component
def Demo():
    bar_style = {"parent": {"width": "500px"}, "data": {"fill": "royalblue"}}
    return VictoryBar({"style": bar_style})


Demo()