Skip to main content

PyPI Version Anaconda-Server Badge Tests Tests

Coverage badge2

Introduction

nbappinator streamlines Jupyter and Voila app development through a structured, opinionated framework for UI construction. Adding a button to a tab is as simple as app.tab(0).button("Run", on_click=callback).

nbappinator has three goals:

  • Simplify UI development for Notebook developers by reducing the surface area of APIs to learn.
  • Abstract the underlying UI components, allowing nbappinator to plug in different frameworks to achieve equivalent behavior.
  • Provide a foundation to develop reusable and portable themes to improve app styling.

Example

Getting Started

import nbappinator as nb

def my_action(app):
    with app.messages:
        print("This message will be written to the Messages footer")
        app.tab(0).label("This is some text")

def choose_action(app):
    with app.messages:
        chosen_value = app["choose1"]
        print(f"You chose {chosen_value}")
        app.tab(1).label(f"You chose {chosen_value}")

# Create a Tabbed UI comprised of three sections:
# "Config" Header, Tabbed Pages: "First Tab" and "Second Tab", and a "Messages" Footer
myapp = nb.App(tabs=["First Tab", "Second Tab"], footer="Messages", header="Config")
myapp.config.label("This is static text in the Config section. Add global settings, buttons and other widgets here.")

# Add a button to First Tab
myapp.tab(0).label("This is the first tab")
myapp.tab(0).button("but1", on_click=my_action, label="Some button")
myapp.tab(0).label("Click the button")

# Add a dropdown selection to Second Tab
myapp.tab(1).select("choose1", options=list(range(10)), on_change=choose_action, label="Choose A Number")

# Render the app:
myapp.display()

API Overview

Creating an App

app = nb.App(
    tabs=["Tab 1", "Tab 2"],  # Required: list of tab names
    header="Config",          # Optional: collapsible header section
    footer="Messages",        # Optional: collapsible footer (default)
    title="My App",           # Optional: browser title
)

Accessing Sections

app.config           # Header section (if configured)
app.tab(0)           # Tab by index
app.tab("Tab 1")     # Tab by name
app.footer           # Footer section
app.messages         # Output widget in footer for print statements

Input Widgets

All input widgets return the page for method chaining.

page.select("name", options=["a", "b"], default="a", on_change=callback)
page.combobox("name", options=["a", "b"])           # Select with text input
page.slider("name", min_val=0, max_val=100, default=50)
page.radio("name", options=["a", "b"], horizontal=True)
page.text("name", default="", multiline=False)
page.checkbox("name", default=False)
page.button("name", on_click=callback, status=True)  # status=True adds progress indicator

Display Widgets

page.label("Static text")
page.pre("Preformatted text")
page.html("<b>HTML content</b>")
page.separator(color="gray")
page.output("name")                # Output area for print statements

Data and Charts

page.dataframe("name", df, on_click=callback)
page.dataframe("name", df, tree=True, tree_column="path", enterprise=True)  # Tree requires enterprise
page.plotly(fig)
page.matplotlib(fig)
page.networkx(graph, layout="force")   # D3 force-directed graph
page.tree("name", paths=["a~b~c"], delimiter="~")  # D3 collapsible tree

Standalone AG Grid

Use create_grid() to create an AG Grid without the App wrapper:

from nbappinator import create_grid, get_column_defs, apply_format, FORMAT_MAG_SI, FORMAT_PERCENT

# Generate column definitions from DataFrame
columns = get_column_defs(df)

# Override
columns[0]["pinned"] = "left"

# Apply built-in formatters (MAG_SI for K/M/B, PERCENT for %)
apply_format(columns[1], FORMAT_MAG_SI, precision=1)   # 1500000 -> "1.5M"
apply_format(columns[2], FORMAT_PERCENT, precision=2)  # 0.05 -> "5.00%"

# Or use custom JS formatters
columns[3]["valueFormatter"] = "params => '$' + params.value.toFixed(2)"

grid = create_grid(
    df,
    column_defs=columns,
    height=400,
    enterprise=False,             # Set True for enterprise features
    license_key="",               # Your AG Grid license key
)
grid

Layout

row = page.row()       # Horizontal container
row.button(...)
col = page.column()    # Vertical container

Callbacks

Callbacks receive the app as their only argument:

def my_callback(app):
    value = app["widget_name"]       # Get widget value
    app["widget_name"] = new_value   # Set widget value
    app.status("Working...")         # Update button status
    app.done("Complete")             # Mark button as done

Button with Status

app.config.button("Run", on_click=run_task, status=True)

def run_task(app):
    app.status("Loading data...")
    # ... do work ...
    app.status("Processing...")
    # ... more work ...
    app.done("Complete")

Examples

Interactive notebooks demonstrating nbappinator features are available in the notebooks/ directory:

Feature Example
Getting Started 1_readme_example.ipynb
Plotly Charts 3_charts_plotly.ipynb
Matplotlib 3_charts_matplotlib.ipynb
D3 Network Graphs 4_networkx_graph.ipynb
Graphviz Graphs 5_graphviz_graph.ipynb
D3 Tree Widget 6_tree_app.ipynb
AG Grid (Community) 7_grid_anywidget.ipynb
AG Grid (Enterprise) 8_aggrid_enterprise.ipynb
Default Grid Renderer 9_default_grid_renderer.ipynb

Deployment and BQuant

nbappinator was originally designed to simplify developing applications within Bloomberg's BQuant environment, which provides a managed but locked down Jupyter environment with a Voila-based deployment of applications.

Acknowledgements

nbappinator builds on some great projects that provide useful building blocks in Jupyter, which themselves build on other great web technologies. At the same time, nbappinator is implementation agnostic - a core goal is to allow any of these components to be swapped out.

Vue.js The Progressive JavaScript Framework

AG Grid is an excellent javascript grid library. nbappinator loads AG Grid via CDN using anywidget, supporting both Community and Enterprise editions.

Plotly is given first class support, although any matplotlib charting library works, such as Seaborn.

D3.js powers the interactive NetworkX graph visualizations and collapsible tree widgets.

This all builds on Jupyter and ipywidgets.

External CDN Dependencies

At runtime, nbappinator loads the following JavaScript libraries from CDN:

  • AG Grid Community/Enterprise from cdn.jsdelivr.net
  • D3.js from cdn.jsdelivr.net
  • Graphviz WASM from cdn.jsdelivr.net (for tree visualizations)

This means an internet connection is required when first rendering widgets that use these libraries. Bundling these dependencies locally for offline/air-gapped use is technically feasible but not yet implemented.

Testing Notes

A significant portion of the tests are Notebook smoketests designed to exercise the code base in its entirety. The coverage report primarily reflects the percentage of the code base that the Notebooks exercise: but manual verification of the Notebook behavior is still required.

Some assertions are baked into the Notebooks, but largely its intended to ensure that all the features are exercised.

Metadata

Release files for nbappinator 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nbappinator 0.3.0
File Size Uploaded
nbappinator-0.3.0.tar.gz 40.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nbappinator 0.3.0
File Interpreter ABI Platform
nbappinator-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 90.6 kB

Release files / nbappinator-0.3.0.tar.gz

Download URL nbappinator-0.3.0.tar.gz
Size 40.0 kB
Tags Source
SHA-256 checksum
How to use checksums
9923534790b07d68c6d2c78e86ec3613ccd3f193dd5dc5cb975051d1504c3917
BLAKE2b-256 checksum
How to use checksums
75b62e10c70d5953a50a73b6ae495622b8b93d5e2118c0ffe5d4f0c345be97cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 2, 2026.

Transparency log

Release files / nbappinator-0.3.0-py3-none-any.whl

Download URL nbappinator-0.3.0-py3-none-any.whl
Size 50.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c4732fa7d1907d9125edf58859d2947d6bf84e8febb4584852ac1707d13d260
BLAKE2b-256 checksum
How to use checksums
bccd0acdb9a9ba62f068f8515ebb7dd97bb4252d866a759535333e3d4bef6f11
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 2, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page