A Python package to convert Streamlit applications into single, self-contained HTML files that run entirely in the browser using stlite.
This allows you to easily share or deploy your Streamlit apps as static web pages without needing a Python backend server.
This project was created to easily allow existing streamlit apps to be converted to stlite web apps. Another GREAT way to generate stlite web apps is using https://edit.share.stlite.net/
The creator of stlite and edit.share.stlite.net is Yuichiro Tachibana (whitphx)
Please read this section carefully before using script2stlite.
Version 0.3.0+ uses an "Include All" strategy for bundling your application. This means:
- ALL FILES in the directory you provide (and its subdirectories) will be bundled into the generated HTML file and publicly accessible to anyone who views the page.
- DO NOT include secrets, API keys, passwords, or sensitive environment files (like
.env,secrets.toml,.pem) in the directory you are converting. - Clean Directory Principle: We strongly recommend creating a dedicated "build" or "dist" directory for your app that contains only the files needed for the app to run.
Default Exclusions: The following files and directories are excluded by default to keep the bundle clean, but you should not rely on this as your primary security control:
- Directories:
.git,__pycache__,venv,.venv,env,.mypy_cache,.pytest_cache,dist,build,.idea,.vscode,node_modules - Files:
.DS_Store,.gitignore,.env
Version 0.3.0 introduces major simplifications to the workflow:
- Auto-Discovery: The converter now automatically bundles all files in your project directory. This fixes issues with dynamic imports, f-strings, and complex asset paths.
- One-Step Conversion: You can now convert an app without creating a
settings.yamlfile manually.
You can convert your app in a single step using the top-level convert_app function.
Prerequisites:
- A folder containing your Streamlit app (e.g.,
app.py). - (Optional) A
requirements.txtfile listing your dependencies (e.g.,pandas,numpy). If your app uses no external packages (other than Streamlit), this is not needed.
Example:
import script2stlite
script2stlite.convert_app(
directory="my_app_folder",
app_name="My Cool App",
entrypoint="app.py"
)This will generate My_Cool_App.html in my_app_folder. It will automatically include helper.py, data.csv, images/logo.png, and any other file present in the folder.
- One-Step Conversion: Convert straight from Python code without configuration files.
- Full Directory Bundling: automatically bundles all files in the directory, ensuring no missing assets or modules.
- Prepare Project Folders: Initialize a directory with the necessary structure (
pagessubdirectory) and a templatesettings.yamlconfiguration file (Legacy mode). - Convert to HTML: Bundle your Streamlit application (main script, pages, requirements, and other assets) into a single HTML file.
- Class-Based Conversion: Offers a
Script2StliteConverterclass for an object-oriented approach to managing the conversion process. - Version Pinning: Allows specifying particular versions of
stliteandPyodideto be used for the conversion.
You can install script2stlite using pip:
pip install script2stlite- Simple first example
- Multi-page app - simple image editor
- Example using requests - Recent daily Bitcoin price
- Simple conversion from existing web app - Streamlit cheat sheet by Daniel Lewis
- More complex app conversion - Streamlit ECharts demo by Fanilo Andrianasolo
- Example with Machine Learning - PixelHue by Juncel Datinggaling
- How to add a config.toml file to control app appearance - Vizzu example by Germán Andrés and Castaño Vásquez
- File Persistence Demo
- IDBFS File Browser
The original workflow using settings.yaml is still fully supported and recommended for complex configurations or when you need explicit control over dependencies.
Note: Even when using this legacy method, the new Full Directory Bundling is active! All files in the directory will be merged with your manual settings.yaml configuration.
First, you'll use the Script2StliteConverter class. Point it to the directory containing the example application. Then, call prepare_folder() to ensure the necessary settings.yaml file is present (it will be created from a template if it doesn't exist) and a pages subdirectory is available.
from script2stlite import Script2StliteConverter
# Initialize the converter with the path to the example app
# If you have cloned the repository, this path will be relative to the repo root.
converter = Script2StliteConverter(directory="example/Example_0_simple_app")
# Prepare the folder. This creates 'settings.yaml' if it's missing
# and ensures a 'pages' directory exists.
# For this example, 'settings.yaml' is already provided.
converter.prepare_folder()The prepare_folder() step ensures a settings.yaml file is in place. For Example_0_simple_app, the settings.yaml file looks like this:
APP_NAME: "my simple app" #give your app a nice name
APP_REQUIREMENTS: #app requirements separated by a '-' on each new line. Requirements MUST be compatible with pyodide. Suggest specifying versions.
- streamlit
- pandas
- numpy
APP_ENTRYPOINT: home.py #entrypoint to app - main python file
CONFIG: "none"
APP_FILES: #each file separated by a '-'. Can be .py files or other filetypes that will be converted to binary and embeded in the html.
- functions.py #additional files for the conversion to find and include.
- assets/image.jpgKey fields in this example:
APP_NAME: "my simple app" - This will be used for the HTML file name and page title.APP_REQUIREMENTS: Listsstreamlit,pandas, andnumpy.APP_ENTRYPOINT:home.py- This is the main script for the Streamlit app.CONFIG:false(or a path like.streamlit/config.toml) - Specifies an optional Streamlit configuration file. If a path is provided, it must point to a TOML file. Set tofalseif no configuration file is used. An example using a TOML config can be found inexample/Example_6_vizzu.APP_FILES: Includesfunctions.py(a supporting Python module) andassets/image.jpg(an image asset).
The main application script for this example is example/Example_0_simple_app/home.py:
import streamlit as st
from functions import random_pandas_dataframe
#say something
st.write("This text is from home.py")
#get a dataframe
st.write("The dataframe below is from functions.py")
df = random_pandas_dataframe()
st.write(df)
#show an image
st.write("The image below in in the assets folder, but is embeded into the html file.")
st.image("assets/image.jpg")This script imports a function from functions.py (which is listed in APP_FILES), displays some text, shows a Pandas DataFrame, and an image.
With the folder prepared and settings reviewed, you can convert the project into a single HTML file using the convert() method:
# Assuming 'converter' is the Script2StliteConverter instance from Step 1.
converter.convert()
# This will read 'settings.yaml' from 'example/Example_0_simple_app',
# bundle all specified files, and generate 'my_simple_app.html'
# inside the 'example/Example_0_simple_app' directory.For more examples (as they become available), please check the example directory in this repository. Each subfolder there will typically contain a self-contained Streamlit application ready for conversion with script2stlite.
script2stlite streamlines the process of packaging your Streamlit application for browser-only execution. Here's a simplified overview:
- Configuration Reading: The tool reads your project's structure and dependencies from the
settings.yamlfile (or arguments passed toconvert_from_entrypoint). This includes your main application script (APP_ENTRYPOINT), any additional pages or Python modules (APP_FILES), and Python package requirements (APP_REQUIREMENTS). - Auto Discovery: It parses your code to automatically find imported modules and assets, and reads
requirements.txtto find dependencies. - File Aggregation: It collects all specified Python scripts, data files, and assets. Python files and text-based data files are read as strings. Binary files (like images) are base64 encoded.
- HTML Generation:
script2stliteuses an HTML template that is pre-configured to usestlite. It injects your application's details into this template:- The content of your main Streamlit script (
APP_ENTRYPOINT) becomes the primary script executed bystlite. - Other Python files and data files from
APP_FILESare embedded into thestlitevirtual filesystem, making them accessible to your application at runtime. - The package
APP_REQUIREMENTSare listed forstliteto install viamicropipfrom Pyodide. - Links to the necessary
stliteCSS and JavaScript bundles, and the specified Pyodide version, are included.
- The content of your main Streamlit script (
- Bundling: The result is a single HTML file. This file contains your entire Streamlit application (code, data, assets) and the
stliteruntime environment. - Browser Execution: When you open this HTML file in a web browser:
stliteinitializes Pyodide, which is a port of Python to WebAssembly.- The specified Python packages are downloaded and installed into the Pyodide environment.
- Your Streamlit application code is executed by the Python interpreter running in the browser.
- Streamlit components are rendered directly in the HTML page, providing the interactive experience.
Essentially, script2stlite automates the setup described in the stlite documentation for self-hosting, packaging everything neatly into one portable HTML file. It leverages stlite's ability to run Streamlit applications without a server by bringing the Python runtime and Streamlit framework into the browser environment.
Since script2stlite relies on stlite and Pyodide, it inherits their limitations. Key considerations include:
-
Pyodide Package Compatibility:
- All Python packages listed in
APP_REQUIREMENTSmust be compatible with Pyodide. This generally means they should be pure Python or have pre-compiled WebAssembly wheels available. - Packages with complex binary dependencies that are not specifically ported to the Pyodide environment will not work.
- For more details on Pyodide package support, see:
- All Python packages listed in
-
Inherited
stliteLimitations:st.spinner(): May not display correctly with blocking methods due to the single-threaded browser environment. A workaround is to useawait asyncio.sleep(0.1)before the blocking call.st.bokeh_chart(): Currently does not work as Pyodide uses Bokeh 3.x while Streamlit supports 2.x.time.sleep(): Is a no-op. Useawait asyncio.sleep()instead (requires usingasyncfunctions and top-level await where necessary).st.write_stream(): Should be used with async generator functions for reliable behavior.- DataFrame Serialization: Minor differences in how some data types in DataFrame columns are handled by
st.dataframe(),st.data_editor(),st.table(), and Altair-based charts, becausestliteuses Parquet for serialization instead of Arrow IPC. - Micropip Version Resolution: Package version resolution by
micropip(used by Pyodide) can sometimes fail or lead to unexpected versions, especially with complex dependencies. - For a comprehensive list of
stlitelimitations, refer to the official stlite documentation.
-
File System and Persistence:
- The default file system (MEMFS) provided by
stlite/Pyodide is ephemeral. Any files written by your application at runtime (e.g., saving a generated file) will be lost when the browser tab is closed or reloaded. - However,
script2stlitesupports persistent storage using IDBFS (IndexedDB File System) directly through thesettings.yamlfile. By specifying one or more directory paths under theIDBFS_MOUNTPOINTSkey, you can ensure that any files written to those paths are saved in the browser's IndexedDB and persist across sessions.
For a hands-on demonstration, see Example 8: File Persistence Demo, which shows how to write to a persistent file.
Additionally, Example 9: IDBFS File Browser illustrates how this persistent storage can be accessed and shared by other
stliteapplications. - The default file system (MEMFS) provided by
-
HTTP Requests:
- Standard Python networking libraries like
socketdo not work directly in the browser. - For making HTTP requests, use Pyodide-specific mechanisms like
pyodide.http.pyfetch()orpyodide.http.open_url(). - The
requestslibrary and parts ofurllibare patched bypyodide-httpto work in many common cases, but some advanced features might not be available. See the stlite documentation on HTTP requests and pyodide-http for details.
- Standard Python networking libraries like
-
Performance:
- Initial load time can be significant, as the browser needs to download Pyodide,
stlite, and your application's packages. - CPU-intensive Python operations will run slower in the browser (via WebAssembly) compared to a native Python environment.
- Initial load time can be significant, as the browser needs to download Pyodide,
-
Browser Environment:
- Direct access to the local file system (outside the virtual Pyodide FS) or system resources is not possible due to browser security restrictions.
You can control the versions of stlite (which also dictates the Streamlit version) and Pyodide used in the generated HTML file. This is useful for ensuring compatibility or using specific features from particular releases.
Pass the stlite_version and/or pyodide_version arguments to the convert method of the Script2StliteConverter class.
from script2stlite import Script2StliteConverter
converter = Script2StliteConverter(directory="my_stlite_app")
# Example: Pin stlite to version 0.82.0 and Pyodide to 0.27.4
# Ensure settings.yaml and app files are ready in "my_stlite_app" first.
# converter.prepare_folder() # if needed
converter.convert(
stlite_version="0.82.0", # Check available stlite versions
pyodide_version="0.27.4" # Check available Pyodide versions compatible with stlite
)script2stlite comes with lists of known compatible versions (see stlite_versions directory in the repository). If you specify a version not listed, it might lead to errors if the CDN links are incorrect or the versions are incompatible. By default, the latest known compatible versions are used.
The Script2StliteConverter.convert() method includes a packages parameter (a dictionary). This parameter is intended for fine-grained control over package versions, potentially overriding what's listed in APP_REQUIREMENTS or how they are formatted for micropip.
Example:
from script2stlite import Script2StliteConverter
converter = Script2StliteConverter(directory="my_stlite_app")
# This is a more advanced use case, typically APP_REQUIREMENTS is sufficient.
# Ensure settings.yaml and app files are ready in "my_stlite_app" first.
# converter.prepare_folder() # if needed
converter.convert(
packages={"pandas": "pandas==1.5.3", "numpy": "numpy>=1.20.0,<1.23.0"}
)In this example, if APP_REQUIREMENTS in settings.yaml just listed pandas and numpy, the packages argument would provide more specific version constraints for micropip.
However, for most use cases, defining your requirements directly in the APP_REQUIREMENTS list in settings.yaml with appropriate version specifiers (e.g., pandas==1.5.3, matplotlib>=3.5) is the recommended approach. The packages parameter offers an override mechanism primarily for scenarios where the user would like to keepa series of stlite apps on the same package versions. In theory this should reduce loading time for users since a single version of a package is downloaded, rather than multiple versions.
For applications where multiple stlite instances might run on the same page, stlite offers a "SharedWorker mode" to conserve resources by running all apps in a single worker.
To enable this mode, set the SHARED_WORKER key to true in your settings.yaml:
SHARED_WORKER: trueWhen this is enabled, script2stlite will configure the stlite.mount() call with the sharedWorker: true option.
Key considerations for SharedWorker mode:
- The Python environment and file system are shared across all apps.
- Package installations and module modifications are shared.
- It may not be supported in all browsers (e.g., Chrome on Android), in which case
stlitewill fall back to the default behavior. - For more details, refer to the stlite documentation on SharedWorker mode.
You can customize various aspects of your Streamlit application's appearance and behavior by providing a config.toml file. This is particularly useful for setting theme options, configuring server behaviors, or defining custom component settings.
To use a configuration file:
- Create a
config.tomlfile in your project, often placed in a.streamlitsubdirectory (e.g.,.streamlit/config.toml). - Specify the path to this file in your
settings.yamlunder theCONFIGkey. For example:If you don't need a configuration file, setCONFIG: .streamlit/config.toml
CONFIG: false.
script2stlite will embed the content of this config.toml file into the generated HTML, making it available to your stlite application.
Example config.toml:
[theme]
primaryColor="#F63366"
backgroundColor="#FFFFFF"
secondaryBackgroundColor="#F0F2F6"
textColor="#262730"
font="sans serif"Key Points:
script2stlitesimply includes theconfig.tomlcontent. The interpretation and application of these settings are handled by Streamlit running withinstlite.- Not all options available in a standard Streamlit deployment might be relevant or function identically in the
stliteenvironment due to its browser-based nature. - For a comprehensive list of all available configuration options, please refer to the official Streamlit documentation on configuration.
- The example application in
example/Example_6_vizzudemonstrates the use of aconfig.tomlfile to set a custom theme for the Vizzu charts.
This repository includes a test suite to ensure the functionality of script2stlite.
To run the tests and generate a datetime-stamped log file, use the provided script:
./run_tests.shThis will execute the test suite using pytest and save a detailed log in the test_logs directory. The log file will be named with the date and time of the test run (e.g., test_logs/test_run_2025-09-02_18-15-00.log).
Contributions are welcome! If you have suggestions for improvements, new features, or bug fixes, please feel free to:
- Open an issue to discuss the change.
- Fork the repository and submit a pull request.
Please ensure that your code adheres to standard Python conventions.
This project is licensed under the MIT License. See the LICENSE file for details.
