Shinylive: Shiny + WebAssembly

Shiny for Python has an experimental mode called Shinylive: this is where applications can run entirely in a web browser, without the need for a separate server running Python.

The traditional way of deploying Shiny involves in a separate server and client: the server runs Python and Shiny, and clients connect via the web browser. Each client keeps an open websocket connection as long as they are using the application.

Traditional Shiny deployment

When an application is deployed with Shinylive, Python and Shiny run in the web browser: the browser is effectively both the client and server for the application. There is a web server that serves files, but it does not run Python or Shiny—it can be a “dumb” static web server.

Shinylive deployment

If you’ve looked at any of the documentation on this web site, or have played with any of the examples at shinylive.io, you have already used Shinylive. The examples on this site (with a handful of exceptions) and the shinylive.io examples all run using Shinylive, meaning that they run in your web browser.

This is all possible because of the magic of WebAssembly and Pyodide.

Applications deployed with Shinylive have some advantages and disadvantages compared to a traditional Shiny deployment. The advantages include:

Some of the disadvantages of using Shinylive deployments compared to traditional Shiny deployments:

For certain types of Shiny applications, some of the limitations can be worked around by pre-processing a data set and including it with the application.

One important difference between traditional Shiny and Shinylive deployments is that compute power is shifted from the server to the client. In many cases, the client browser will have more compute power than a server, especially since the compute power of the user’s machine is not shared across multiple users. However, in other cases, this can be a roadblock, such as when a powerful server is needed to perform very intensive computations or requires access to a private data store.

If you want to do a traditional Shiny application deployment, see the Deploy page.

Sharing and deploying Shinylive applications

In this document, we’ll use the terms sharing and deploying Shiny applications. When we talk about sharing, we’re referring to a method of encoding the application in a URL so that others can run the application if they simply have the URL. Sharing an application via a URL does not require you to have a server—you can simply use the server at shinylive.io.

When we talk about deploying applications, we mean creating a set of files which are to be served up by a web server. This does require you to have a web server. For a traditional Shiny deployment, this means having a server that runs R or Python, and is covered in the Deploying Shiny page. For a Shinylive deployment, this only requires a server that can serve static files—it can be a “dumb” web server which does not run Python. For example you could deploy your application to GitHub Pages or Netlify.

Sharing Shinylive applications

The easiest way to share an application is to create it on the Shinylive editor, and then click on the “Create share link” button. This will encode the application in a URL, which you can then share with others.

Share button

The dialog box that appears will provide two links: one for the application in the Shinylive editor, and one with for the application running standalone.

Share URLs

Here is an example of a Shiny application that is encoded in a share URL. This will lead to the application with an editor and Python console:

https://shinylive.io/py/editor/#code=NobwRAdghgtgpmAXGKAHVA6VBPMAa…

If you want to share just the Shiny application, without the editor and console, use the other link, which contains /app/ instead of /editor/:

https://shinylive.io/py/app/#code=NobwRAdghgtgpmAXGKAHVA6VBPMAa…

These URLs have a hash that includes #code=.... The code for the entire application is encoded in that hash. Notably, web browsers do not send the hash to the web server, so the server actually never sees the content of the Shiny application.

The sharing dialog shows how long the URL is, in bytes. If you want to share a link on Twitter, the maximum length of a URL is about 4000 bytes, and it will be shortened using their t.co service. If you use bit.ly, the maximum length is about 2000 bytes. These link shorteners redirect the user to the longer URL.

Sharing with gists

Another way of sharing Shinylive applications is by using a GitHub gist. For example, the gist here:

https://gist.github.com/wch/e62218aa28bf26e785fc6cb99efe8efe

Can be run with Shinylive here:

Notice that the #gist=... part of the URL simply uses the ID of the gist.

To create a gist, you can go to gist.github.com/, or you can use GitHub’s gh command-line tool to create a gist from files on disk. To do that, first install gh, then use gh gist create:

gh gist create --public app.py

Sharing via gists has some important differences from sharing via encoded-app URL. If you use a gist, you can modify the gist, and the sharing URL will stay the same. If you are sharing an encoded-app URL, the URL itself contains the application code, so if you want modify the code, you will have to generate a new URL and share that.

Sharing via GitHub gist may not be appropriate for all use cases, because the GitHub API has rate limits: for a given IP address, the GitHub API allows 60 requests per hour. So an end user would only be able to load Shinylive applications 60 times in an hour. And if there are many users behind a single IP address with network address translation, they collectively would have a limit of 60 requests per hour.

If you are using GitHub gist for sharing, you can see your remaining requests at https://api.github.com/rate_limit.

Note

The GitHub API has a much higher rate limit if the end user is authenticated, but Shinylive currently does not support authenticating with GitHub.

Deploying Shinylive applications

Deplying a Shinylive application involves:

  • Creating a directory of files that includes the Shinylive distribution and the application code.
  • Deploying that directory to a static web server.
Note

This is experimental, and the details may change in the future.

There are many ways to deploy to a static web server. For example, you could deploy to Netlify or GitHub Pages, or use RStudio Connect, as described later in this page.

First, create a directory with a Shiny application. We’ll use the shiny create command to create a basic application in a directory called myapp/.

shiny create myapp

Next, create the distribution with shinylive:

shiny static myapp site

The resulting site directory will contain the following files (among others that are not shown for brevity):

site
├── app.json          # The application's files serialized to JSON
├── index.html        # A web page for the application
├── edit
│   └── index.html    # A web page for an editor view of the application
├── serviceworker.js  # Shinylive service worker
└── shinylive         # Shinylive content
    └── pyodide       # Pyodide files

This directory can now be deployed to a static web hosting service.

You can preview the application by serving the files in the site directory:

python3 -m http.server --directory site 8000

This will serve the files in the site directory on port 8000. Then point your browser at http://localhost:8000/. You can also see the application with an online editor by pointing your browser at http://localhost:8000/edit/. (Note that any changes to the files there are ephemeral—they won’t be saved to disk.)

Note

To run a Shinylive application, the files must be served with a web server; simply pointing your browser to the files on disk will not work. This is because security restrictions in web browsers require some assets to be retrieved from a web server instead of from disk.

If you have multiple applications, you may want to deploy them in subdirectories of the site, so that they can all share the same Shinylive assets. You can do this with the --subdir option:

shiny static myapp1 site --subdir app1
shiny static myapp2 site --subdir app2

The site/shinylive/pyodide/ directory will contain a Pyodide distribution containing just the Python packages needed to run the deployed application(s). There are some cases where you may want to include other packages. For example, if you want users who visit the edit/ URL to be able to load more packages. In order to include extra packages, you have two options:

  • Add a requirements.txt file to an application which lists the extra packages.
  • Run shiny static myapp site --full-shinylive. This will cause it to include all of the Python packages from the Shinylive distribution.
Note

The Shinylive distribution is under rapid development, and the files in the distribution will change. The shiny static command automatically downloads and caches a a copy of the Shinylive distribution on your computer. To make sure you are up to date, run:

pip install shiny --upgrade
shiny static-assets remove   # Remove old cached shinylive files

Then the next time you run shiny static, it will download the latest version.

Deploying to RStudio Connect

After creating the directory with the application and Shinylive bundle, you can deploy it to many different of static web hosting services. RStudio Connect is one of those options, and allows you to control over who can access the application.

If you would like to deploy to a RStudio Connect server, install and configure the rsconnect-python package as described in the Deploy page. Then you can deploy the application as a static website:

rsconnect deploy html site

Python packages

The Shinylive distribution is built on Pyodide, and contains a number of additional packages on top of the standard Pyodide distribution.

It is also possible to use other Python packages, provided that they are packaged as wheels, and contain no compiled code. Additionally, they must not use features that aren’t available in Pyodide. For example, if a package has code that uses urllib.request, it won’t work in Pyodide.

Installed packages

The Shinylive distribution includes packages from Pyodide, as well as additional packages See this page for a list of packages included in Pyodide.

In addition to the packages from Pyodide, Shinylive includes the following:

Package Version
anyio 3.6.1
appdirs 1.4.4
asgiref 3.5.2
black 22.6.0
click 8.1.3
h11 0.13.0
htmltools 0.1.2
idna 3.3
ipyleaflet 0.17.0
ipython-genutils 0.2.0
ipywidgets 7.7.1
jupyter-core 4.11.1
linkify-it-py 2.0.0
markdown-it-py 2.1.0
mdit-py-plugins 0.3.0
mdurl 0.1.1
mizani 0.7.4
mypy-extensions 0.4.3
palettable 3.3.0
palmerpenguins 0.1.4
pathspec 0.9.0
platformdirs 2.5.2
plotly 5.9.0
plotnine 0.9.0
python-multipart 0.0.4
seaborn 0.11.2
shiny 0.2.5
shinywidgets 0.1.2
siuba 0.3.0
sniffio 1.2.0
starlette 0.20.4
tenacity 8.0.1
tomli 2.0.1
traitlets 5.3.0
traittypes 0.2.1
uc-micro-py 1.0.1
uvicorn 0.18.2
xyzservices 2022.6.0

Testing whether a package is available

The Shinylive distribution includes many packages, but you may want to use one that is not included.

It is possible to install packages using Pyodide’s micropip package. To do that, simply visit the Shinylive examples page and run the following in the Python console:

import micropip
micropip.install("mypackage")
import mypackage

If that works without errors, then your package is usable in a Shinylive application. (There are some exceptions, where a package will load but not be fully usable in Pyodide.)

The micropip.install command will install the package from PyPI by default. However, you can provide a URL that points directly to your package, like https://example.com/mypackage-1.0-py3-none-any.whl.

Requiring extra packages with requirements.txt

To use extra packages as part of your application, you can add a requirements.txt file to your application, as demonstrated in the extra packages example. The format of the requirements.txt file is similar to a “normal” requirements.txt file. For example, it could look like this:

isodate
attrs==21.4.0

Each time someone runs your Shiny application, their web browser will fetch those packages from PyPI. It will then install the packages to a virtual file system (VFS); when the user closes the page or navigates away from it, the VFS is discarded. If the user goes back and runs the application again, those files can be fetched from the browser cache instead of from PyPI.