Skip to content

@stlite/desktop

@stlite/desktop allows you to convert your Streamlit application into a desktop app with Stlite runtime, a Pyodide-based Wasm-port of Streamlit.

Stlite Desktop Banner

Create a package.json file for your new project:

package.json
{
"name": "my-app",
"version": "0.1.0",
"main": "./build/electron/main.js",
"scripts": {
"dump": "dump-stlite-desktop-artifacts",
"serve": "cross-env NODE_ENV=production electron .",
"app:dir": "electron-builder --dir",
"app:dist": "electron-builder",
"postinstall": "electron-builder install-app-deps"
},
"build": {
"files": [
"build/**/*"
],
"directories": {
"buildResources": "assets"
}
},
"devDependencies": {
"@stlite/desktop": "^0.92.1",
"cross-env": "^7.0.3",
"electron": "34.3.0",
"electron-builder": "^25.1.8"
},
"stlite": {
"desktop": {
"files": [
"app.py"
],
"entrypoint": "app.py"
}
}
}

Run installation:

Terminal window
npm install
# or
yarn install

Create app.py:

app.py
import streamlit as st
st.write("Hello from Stlite Desktop!")

Dump artifacts (prepare build):

Terminal window
npm run dump

Preview:

Terminal window
npm run serve

Distribute:

Terminal window
npm run app:dist

This generates the application files (.app, .exe, etc.) in the dist directory.

Configure your app via the stlite.desktop section in package.json.

"stlite": {
"desktop": {
"files": ["app.py", "pages/*.py", "assets"],
"entrypoint": "app.py",
"dependencies": ["numpy", "pandas"],
"requirementsTxtFiles": ["requirements.txt"]
}
}
  • files: Files and directories to include.
  • entrypoint: The main script to run.
  • dependencies: List of packages to install.
  • requirementsTxtFiles: List of requirements.txt files to install.

Hide the toolbar and hamburger menu (Embed mode):

"stlite": {
"desktop": {
"embed": true
}
}

Mount an IndexedDB-backed directory to persist files across restarts.

"stlite": {
"desktop": {
"idbfsMountpoints": ["/mnt"]
}
}

To access the host OS file system, you need to enable NodeJS Worker Mode and mount directories using nodefsMountpoints.

"stlite": {
"desktop": {
"nodeJsWorker": true,
"nodefsMountpoints": {
"/mnt": "."
}
}
}

This mounts the current directory (.) of the host to /mnt in the virtual file system.

[!TIP] You can use placeholders like {{home}}, {{userData}}, {{temp}} in the host paths.

By default, Stlite executes Python code on Pyodide running in a Web Worker dispatched by the renderer process. The renderer process is a browser process, so it’s sandboxed from the host OS.

When you set nodeJsWorker to true, Stlite dispatches the worker as a Node.js Worker Thread that runs in the main process. This enables features that require direct access to the host system, such as NODEFS.

"stlite": {
"desktop": {
"nodeJsWorker": true
}
}

In NodeJS worker mode, the Python code has full access to the Node.js JavaScript environment through Pyodide’s js module and pyodide.code.run_js(). This allows you to call any Node.js API directly from Python.

For example, you can execute shell commands:

import pyodide
result = pyodide.code.run_js('''
require("child_process").execSync("whoami").toString().trim()
''')
print(result) # Prints the current username

This opens up powerful possibilities for desktop applications, such as:

  • Running system commands and scripts
  • Accessing Node.js native modules
  • Integrating with other system tools and services

However, this also means your Python code has the same level of access as any Node.js application running on the user’s machine. See the Security Considerations below.

When NodeJS worker mode is enabled, the Python code executes within a Node.js worker thread in the main process. This means:

  • The Python code has the same privileges as the Node.js process itself. It can access the file system, spawn child processes, make network requests, and perform any operation that Node.js can do.
  • There is no sandbox. Unlike the default Web Worker mode (which runs in a browser-like sandboxed environment), NodeJS worker mode provides no isolation between your Python code and the host operating system.
  • You are responsible for the security of your app. If your app executes untrusted Python code or loads untrusted packages, those could potentially harm the user’s system.

This is an intentional design to enable powerful features like direct file system access via NODEFS. However, you should only use this mode when you trust all Python code and packages that your app will execute.

  • External Links: Navigation to external URLs (e.g. st.markdown("[link](https://...)")) is restricted for security.