Skip to main content

shinywidgets

Render ipywidgets inside a Shiny app.

Installation

pip install shinywidgets

Overview

Every Shiny app has two main parts: the user interface (UI) and server logic. {shinywidgets} provides output_widget() for defining where to place a widget in the UI and register_widget() (or @render_widget) for supplying a widget-like object to the output_widget() container. More technically, widget-like means:

  • Any object that subclasses {ipywidgets}'s Widget class.
  • Some other widget-like object that can be coerced into a Widget. Currently, we support objects from {altair}, {bokeh}, and {pydeck}, but please let us know about other packages that we should support.

The recommended way to incorporate {shinywidgets} widgets into Shiny apps is to:

  1. Initialize and register_widget() once for each widget.
    • In most cases, initialization should happen when the user session starts (i.e., the server function first executes), but if the widget is slow to initialize and doesn't need to be shown right away, you may want to delay that initialization until it's needed.
  2. Use Shiny's @reactive.Effect to reactively modify the widget whenever relevant reactive values change.
  3. Use {shinywidgets}'s reactive_read() to update other outputs whenever the widget changes.
    • This way, relevant output(s) invalidate (i.e., recalculate) whenever the relevant widget attributes change (client-side or server-side).

The following app below uses {ipyleaflet} to demonstrate all these concepts:

from shiny import *
from shinywidgets import output_widget, register_widget, reactive_read
import ipyleaflet as L

app_ui = ui.page_fluid(
    ui.input_slider("zoom", "Map zoom level", value=4, min=1, max=10),
    output_widget("map"),
    ui.output_text("map_bounds"),
)

def server(input, output, session):

    # Initialize and display when the session starts (1)
    map = L.Map(center=(52, 360), zoom=4)
    register_widget("map", map)

    # When the slider changes, update the map's zoom attribute (2)
    @reactive.Effect
    def _():
        map.zoom = input.zoom()

    # When zooming directly on the map, update the slider's value (2 and 3)
    @reactive.Effect
    def _():
        ui.update_slider("zoom", value=reactive_read(map, "zoom"))

    # Everytime the map's bounds change, update the output message (3)
    @output
    @render.text
    def map_bounds():
        b = reactive_read(map, "bounds")
        lat = [b[0][0], b[0][1]]
        lon = [b[1][0], b[1][1]]
        return f"The current latitude is {lat} and longitude is {lon}"

app = App(app_ui, server)

The style of programming above (display and mutate) is great for efficiently performing partial updates to a widget. This is really useful when a widget needs to display lots of data and also quickly handle partial updates; for example, toggling the visibility of a fitted line on a scatterplot with lots of points:

from shiny import *
from shinywidgets import output_widget, register_widget
import plotly.graph_objs as go
import numpy as np
from sklearn.linear_model import LinearRegression

# Generate some data and fit a linear regression
n = 10000
d = np.random.RandomState(0).multivariate_normal([0, 0], [(1, 0.5), (0.5, 1)], n).T
fit = LinearRegression().fit(d[0].reshape(-1, 1), d[1])
xgrid = np.linspace(start=min(d[0]), stop=max(d[0]), num=30)

app_ui = ui.page_fluid(
    output_widget("scatterplot"),
    ui.input_checkbox("show_fit", "Show fitted line", value=True),
)

def server(input, output, session):

    scatterplot = go.FigureWidget(
        data=[
            go.Scattergl(
                x=d[0],
                y=d[1],
                mode="markers",
                marker=dict(color="rgba(0, 0, 0, 0.05)", size=5),
            ),
            go.Scattergl(
                x=xgrid,
                y=fit.intercept_ + fit.coef_[0] * xgrid,
                mode="lines",
                line=dict(color="red", width=2),
            ),
        ]
    )

    register_widget("scatterplot", scatterplot)

    @reactive.Effect
    def _():
        scatterplot.data[1].visible = input.show_fit()

app = App(app_ui, server)

That being said, in a situation where:

  • Performant updates aren't important
  • Other outputs don't depend on the widget's state
  • It's convenient to initialize a widget in a reactive context

Then it's ok to reach for @render_widget() (instead of register_widget()) which creates a reactive context (similar to Shiny's @render_plot(), @render_text(), etc.) where each time that context gets invalidated, the output gets redrawn from scratch. In a simple case like the one below, that redrawing may not be noticable, but if you we're to redraw the entire scatterplot above everytime the fitted line was toggled, there'd be noticeable delay.

from shiny import *
from shinywidgets import output_widget, render_widget
import ipyleaflet as L

app_ui = ui.page_fluid(
    ui.input_slider("zoom", "Map zoom level", value=4, min=1, max=10),
    output_widget("map")
)

def server(input, output, session):
    @output
    @render_widget
    def map():
        return L.Map(center=(52, 360), zoom=input.zoom())

app = App(app_ui, server)

Frequently asked questions

How do I size the widget?

{ipywidgets}' Widget class has it's own API for setting inline CSS styles, including height and width. So, given a Widget instance w, you should be able to do something like:

w.layout.height = "600px"
w.layout.width = "80%"

How do I hide/show a widget?

As mentioned above, a Widget class should have a layout attribute, which can be used to set all sorts of CSS styles, including display and visibility. So, if you wanted to hide a widget and still have space allocated for it:

w.layout.visibility = "hidden"

Or, to not give it any space:

w.layout.display = "none"

Can I render widgets that contain other widgets?

Yes! In fact this a crucial aspect to how packages like {ipyleaflet} work. In {ipyleaflet}'s case, each individual marker is a widget which gets attached to a Map() via .add_layer().

Does {shinywidgets} work with Shinylive?

Shinylive allows some Shiny apps to be statically served (i.e., run entirely in the browser). py-shinylive does have some special support for {shinywidgets} and it's dependencies, which should make most widgets work out-of-the-box.

In some cases, the package(s) that you want to use may not come pre-bundled with {shinywidgets}; and in that case, you can include a requirements.txt file to pre-install those other packages

Troubleshooting

If after installing {shinywidgets}, you have trouble rendering widgets, first try running the "hello world" ipywidgets example. If that doesn't work, it could be that you have an unsupported version of a dependency like {ipywidgets} or {shiny}.

If you can run the "hello world" example, but "3rd party" widget(s) don't work, first check that the extension is properly configured with jupyter nbextension list. Even if the extension is properly configured, it still may not work right away, especially if that widget requires initialization code in a notebook environment. In this case, {shinywidgets} probably won't work without providing the equivalent setup information to Shiny. Some known cases of this are:

bokeh

To use {bokeh} in notebook, you have to run bokeh.io.output_notebook(). The equivalent thing in Shiny is to include the following in the UI definition:

from bokeh.resources import Resources
head_content(HTML(Resources(mode="inline").render()))

Other widgets?

Know of another widget that requires initialization code? Please let us know about it!

Development

If you want to do development on {shinywidgets}, run:

pip install -e ".[dev,test]"
cd js && yarn watch

Release files for shinywidgets 0.1.6

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

Source distribution (sdist)

Source distribution for shinywidgets 0.1.6
File Size Uploaded
shinywidgets-0.1.6.tar.gz 1.8 MB Details

Built distribution (wheel)

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

Total release size: 3.6 MB

Release files / shinywidgets-0.1.6.tar.gz

Download URL shinywidgets-0.1.6.tar.gz
Size 1.8 MB
Tags Source
SHA-256 checksum
How to use checksums
d4b034d41e863df64fb50236210efd78528239d0cdf5a163ab1fd700733ab8d4
BLAKE2b-256 checksum
How to use checksums
44c442eb26b2ff9b3b2e7708728de4d15db89db6e59b67ee592e9d01780635e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.9.16

Release files / shinywidgets-0.1.6-py3-none-any.whl

Download URL shinywidgets-0.1.6-py3-none-any.whl
Size 1.8 MB
Tags Python 3
SHA-256 checksum
How to use checksums
326b5edaf8ad9194e728247a57c3e70c0a13b04d6a0a8daa2730c439c7d76c8d
BLAKE2b-256 checksum
How to use checksums
c5bd1a15c2e5dd037bac4f0838c7185e632024ae26f2a1b64f031768cf36501b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.9.16

Release history Release notifications | RSS feed

0.8.1

2 release files

0.8.0

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

This release

0.1.6 This release

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

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