Note: exporting to .exe can be challenging, we recommend hosting your app, click here to learn more.

Streamlit is an open souce framework for converting data scripts into shareable web applications. It allows data scientists and machine learning engineers to build intuitive interfaces without having to know web development. In this tutorial we’ll learn some approaches of converting a Streamlit application to a double-click executable file for Windows / Linux / Mac platforms.

The main motivation behind this is that users should be able to bundle the Streamlit application and all of its dependencies into a single package. This bundled package can be easily shared with other users who do not have Streamlit or even Python installed in their machines.

Let’s create a simple streamlit application. Create a file streamlit_app.py with the below code:

import streamlit as st

x = st.slider("Select a value")
st.write(x, "squared is", x * x)

This application can be run from the terminal using the below command:

streamlit run streamlit_app.py

Let’s see two frameworks for converting an application to a desktop application: Nativefier and Stlite. Both of these packages are based on Electron. Electron is a platform that easily enables writing cross-platform desktop applications using JavaScript, HTML, and CSS. Today, some of the most popular desktop apps are entirely written in Electron such as Atom, Visual Studio Code, Slack, etc.

Nativefier

Nativefier is a command-line tool that easily creates a desktop app for any web site with minimal configuration. Apps are wrapped by Electron in an OS executable (.app, .exe, etc.) for use on Windows, OSX and Linux. Note that converting Streamlit applications using Nativefier requires users to deploy the application to Streamlit Share.

Deploy your application in Streamlit Share and copy the URL of the deployed app as shown below:

img

Install nativefier by running the below command:

npm install -g nativefier

Now convert your Streamlit application like so:

nativefier --name '<app.exe name>' '<streamlit sharing website url>' --platform <'windows' or 'mac' or 'linux'>

This will create the exe file in the current directory.

Package versions

Streamlit 1.19.0

Nativefier 50.0.1

Stlite

Stlite is a WebAssembly port of Streamlit that runs on Pyodide runtime. Pyodide is an experimental project from Mozilla to create a full Python data science stack that runs entirely in the browser. A Streamlit application can be converted to an exe using Stlite desktop. This approach does not require you to deploy the Streamlit application to Streamlit share.

First create a package.json file to start a new NPM Project. You may edit the name field.

{
    "name": "streamlit_app_exe",
    "version": "0.1.0",
    "main": "./build/electron/main.js",
    "scripts": {
        "dump": "dump-stlite-desktop-artifacts",
        "serve": 'NODE_ENV="production" electron .',
        "pack": "electron-builder --dir",
        "dist": "electron-builder",
        "postinstall": "electron-builder install-app-deps",
    },
    "build": {"files": ["build/**/*"], "directories": {"buildResources": "assets"}},
    "devDependencies": {
        "@stlite/desktop": "^0.25.0",
        "electron": "23.1.1",
        "electron-builder": "^23.6.0",
    },
}

Install Npm as follows:

npm install

Then create a directory to contain the application files, e.g., streamlit_app. So the path of the main app file looks like streamlit_app/streamlit_app.py

Run the dump command to create the ./build folder.

npm run dump streamlit_app

The dist command bundles the ./build directory created in the step above into application files (.app, .exe, .dmg etc.) in the ./dist directory.

npm run dist

Package versions

Streamlit 1.19.0

Stlite desktop 0.25.0

PyInstaller

PyInstaller bundles a Python application and all its dependencies into a single package. The user can run the packaged app without installing a Python interpreter or any modules.

Let’s see how you can bundle a Streamlit application using PyInstaller.

Create a wrapper code run.py to run the main application.

import streamlit

import streamlit.web.cli as stcli
import os, sys


def resolve_path(path):
    resolved_path = os.path.abspath(os.path.join(os.getcwd(), path))
    return resolved_path


if __name__ == "__main__":
    sys.argv = [
        "streamlit",
        "run",
        resolve_path("streamlit_app.py"),
        "--global.developmentMode=false",
    ]
    sys.exit(stcli.main())

Create a hooks file ./hooks/hook-streamlit.py:

from PyInstaller.utils.hooks import copy_metadata

datas = copy_metadata("streamlit")

Now call PyInstaller as below:

pyinstaller --onefile --additional-hooks-dir=./hooks run.py --clean

This will generate build and dist folders and a run.spec file. Edit the run.spec file to ensure paths are set properly as below:

from PyInstaller.utils.hooks import collect_data_files
from PyInstaller.utils.hooks import copy_metadata

datas = [("{$YOURPYTHONENV}/site-packages/streamlit/runtime", "./streamlit/runtime")]
datas += collect_data_files("streamlit")
datas += copy_metadata("streamlit")


block_cipher = None


a = Analysis(
    ["run.py"],
    pathex=["."],
    binaries=[],
    datas=datas,
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(...)
exe = EXE(...)
coll = COLLECT(...)

Once this command runs, the exe can be found in the path dist/run.exe.

Execute the PyInstaller command one more time to incorporate the above changes:

pyinstaller run.spec --clean

Ensure the streamlit_app.py is copied to the application path and run the run.exe again.

Package versions

Streamlit 1.19.0

PyInstaller 5.8.0

Limitations

During my testing, I ran into several installation issues so I highly recommend you hosting your Streamlit app as a web application rather than exporting to EXE. You can host Streamlit apps for free in our platform. If you want to keep your app private, there are a few approaches: adding password protection or advanced authentication with Auth0.