stlitepack Quickstart Guide
This guide walks you through packaging a Streamlit app into a static stlite app and publishing it on GitHub Pages.
Stlite is a WebAssembly-powered reimplementation of Streamlit that runs entirely in the browser using Pyodide. This means Streamlit apps can be distributed as static HTML files without requiring a Python backend server.
The stlitepack package helps automate the process of packaging your existing Streamlit apps into a stlite-compatible format, so you can publish and share them easily while writing and previewing your Streamlit apps like normal.
Compared to traditional server-deployed Streamlit apps, where the actual computation happens on a web server somewhere, and hosting options for such apps like Streamlit Community Cloud, Pyodide-powered stlite apps offer some unique advantages:
- they run fully client-side (no server costs, no backend setup)
- because of this, you also don’t have to worry about managing scaling or loads to manage concurrent users (within reason!)
- they are privacy-preserving (all data stays in the user’s browser)
- they can be hosted on simple static file hosting services like GitHub Pages
- they don’t ‘go to sleep’ like apps hosted on streamlit community cloud
Why stlitepack?
stlite requires saving your streamlit app code into a html file, with some additional html code before and after the app to ensure various things are loaded in.
However, this makes it harder to
- test your app (as you can no longer run it with
streamlit run
) - debug your app (as you no longer get the standard hinting, error highlighting and syntax colouring you would in a .py file)
- manage complex apps (as growing codebases lead to very large file)
When you then come to host it on a site like github pages, you have to remember extra steps like
- ensuring your html file was called the right thing so github pages could find it
- creating a
.nojekyll
file to avoid extra processing being done to your app
stlitepack
grew out of a need to be able to develop Streamlit apps as normal, and then streamline the process of getting them up and hosted as quickly as possible. With just two key commands – pack()
and setup_github_pages()
- you can take a streamlit app and turn it into a hosted stlite app on github pages.
Installation
stlitepack
is available on pypi (conda forge coming soon!)
pip install stlitepack
Packing Your App
To pack your app, make a new .py file in the same folder as your app.
This is easiest if your main app file is in the root (top-level) folder of your repository - if it is not, check out this packing file for an example of how to deal with it.
The pack
function converts a Streamlit app into a single index.html
file that can be served as a static website.
Example
from stlitepack import pack
pack(="streamlit_app.py"
app_file )
Running this will create a new index.html file that is a full stlite app:
docs/index.html
The package handles all the additional boilerplate html, as well as applying some common stlite-specific corrections that ensure features like streamlit spinners and material-ui icons work correctly.
By default, the index.html file is put in the docs folder to keep everything tidy, and because this is a folder github pages can easily be configured to look for.
Alternatively, you may wish for the output index.html file to be placed in a different subfolder if your chosen static hosting site expects it to be somewhere else.
from stlitepack import pack
pack(="streamlit_app.py",
app_file="my_other_folder"
output_dir )
This will create:
my_other_folder/
└── index.html
If you want to put it one level up from the current location, you can make your packing file in the same location as the app file (making sure you then run the packing file from that location using the cd
command or similar), and then use ".."
as the path to tell the package to place the output file one level up.
from stlitepack import pack
pack(="streamlit_app.py",
app_file=".."
output_dir )
This will create:
index.html
app/
└── streamlit_app.py
└── my_packing_file.py
Note that if you are hosting on github pages and enabling this via the repository settings, there are only two options for where the index.html file can be stored
- your repository’s ‘root’ folder (i.e. the top-level folder you see when you first open up your repository on github)
- the docs folder, which must be directly within the root folder
If you open index.html directly in your browser (i.e. via file://), you may see errors like:
Failed to execute 'pushState' on 'History'
GET http://stlite.invalid/... net::ERR_NAME_NOT_RESOLVED
Don’t worry! These errors are caused by browser security restrictions and do not indicate a problem with the app.
To run the app correctly, you need to serve it over HTTP/HTTPS.
If you add the argument run_preview_server=True
to your packing function, the app will automatically be previewed - though remember to close the server with CTRL + C if you’ve got extra steps in your packing file.
Alternatively, you can start the server yourself:
Once served over HTTP, navigation and media files will work as expected.
Deploying the app to GitHub Pages or any web server will automatically avoid these errors.
Key Parameters
To package up a Python app, the minimum we need to provide is the name of the main Streamlit app file (which is also known as the ‘entrypoint’). This can be called anything, but must be a .py file.
If your app has multiple additional pages stored in a pages
subfolder at the same level as your main app file, then they will automatically be included in the output file.
Main Optional Parameters
requirements
You may wish to install some additional python packages.
- a list of Python package names (
["pandas", "numpy"]
), or - a path to a
requirements.txt
file.
e.g.
from stlitepack import pack
pack(="streamlit_app.py",
app_file=["matplotlib", "numpy"],
requirements )
or
from stlitepack import pack
pack(="streamlit_app.py",
app_file="requirements.txt",
requirements )
If omitted, no extra packages are installed.
Note that due to the way packages are managed in stlite, using pyodide and micropip, you may* not be able to ‘pin’ certain dependencies in the way you are used to with streamlit apps.
More detail about this will be added soon.
In particular, it’s not possible to specify a version of the Streamlit package - this will be ignored by stlite!
Pandas, numpy, scipy and other common packages that are not pure python are other packages where issues may be encountered.
title
This controls the name of the app that will be displayed when a user has it open in a web browser, showing up as the name of the tab.
from stlitepack import pack
pack(="streamlit_app.py",
app_file="My Amazing App"
title )
stylesheet_version
/ js_bundle_version
Version numbers of the stlite JavaScript and CSS bundles. Must be valid versions from the stlite releases page. This will control the Streamlit version that is used too.
The package will default to “0.80.5”, which is known to be particularly stable.
from stlitepack import pack
pack(="streamlit_app.py",
app_file# It's highly recommended to use the same version for both arguments!
="0.84.1",
stylesheet_version="0.84.1"
js_bundle_version )
If you want to see the list of valid stlite versions:
from stlitepack.pack import get_stlite_versions
get_stlite_versions()
extra_files_to_embed
Extra files (relative paths) to mount inside the app, e.g. a .streamlit/config.toml
or extra helper scripts.
e.g.
from stlitepack import pack
pack(="streamlit_app.py",
app_file=[".streamlit/config.toml", "data.csv"]
extra_files )
Note that this must be passed as a list of strings (even if you’re only adding one extra file this way).
Also note that you don’t need to manually add extra app pages from your pages
subfolder this way - they will automatically be picked up when you pass in the path of your main/entrypoint file.
At present, this only really works with text-based files, like .py, .toml, .yml, .csv, etc.
It will try to convert binary file types like .jpg, .png, .mp4 etc. into an embeddable format, but this is experimental and will increase the size of your index.html file significantly
If you need to include these kinds of files, it’s recommended to use extra_files_to_link
instead.
extra_files_to_link
Additional files to hyperlink to. - If passed as a list, must be used with the argument ‘prepend_github_path’. The list must be a list of relative filepaths. - If passed as a dict, expects key:value pairs of relative_filepath: url
e.g. {‘img/my_img.png’: ’https://raw.githubusercontent.com/your-github-username/your-github-repository/refs/heads/main/img/my_img.png}
prepend_github_path
If files to be linked are stored on github, you can pass them as relative paths in the ‘extra_files_to_link’ argument, and then use this argument to automatically generate the url links to the files in your repository.
Needs to passed in the format “username/reponame” e.g. a username of Bergam0t with a reponame of my-streamlit-app would be “Bergam0t/my-streamlit-app”
Then, for example, if you passed a list of [‘img/my_img.png’] to the ‘extra_files_to_link’ argument, it would automatically generate the url of https://raw.githubusercontent.com/Bergam0t/my-streamlit-app/refs/heads/main/img/my_img.png
github_branch
If files to be linked to on Github need to come from a branch other than main, provide the name of the desired branch here. e.g. a username of Bergam0t with a reponame of my-streamlit-app would be “Bergam0t/my-streamlit-app”
If you passed ‘dev’ as your branch to this argument, and then passed a list of [‘img/my_img.png’] to the ‘extra_files_to_link’ argument, it would automatically generate the url of https://raw.githubusercontent.com/Bergam0t/my-streamlit-app/refs/heads/dev/img/my_img.png
run_preview_server
Setting run_preview_server
to true will automatically preview your stlite app in a new browser window so you can check it works as expected.
Rremember to close the server with CTRL + C if you’ve got extra steps in your packing file.
Publishing with GitHub Pages
Use setup_github_pages()
to scaffold a GitHub Pages deployment for your packaged stlite app.
It creates the necessary files and prints step-by-step instructions (also saved to PAGES_SETUP.md
).
Prerequisites
- You have a GitHub account and a repository for your app.
- You know which branch you’ll publish from (default:
main
).
- Repositories are project folders hosted on GitHub. You’ll need to push your code there first.
- Branches are parallel versions of your repository (most often
"main"
). - GitHub Pages is a free hosting service for static sites from your repo.
Typical workflow
from stlitepack import pack, setup_github_pages
# 1) Pack your app into docs/index.html (recommended for Pages)
pack(="streamlit_app.py",
app_file=["matplotlib"],
requirements="docs"
output_dir
)
# 2) Scaffold GitHub Pages
setup_github_pages(="gh-actions", # or "manual"
mode=True, # serve from /docs (recommended) - matches the pack default
use_docs=True, # only trigger a pages rebuild when index.html changes
only_on_index="main"
branch
)
# 3) Follow the printed instructions (also saved to PAGES_SETUP.md).
What the function creates
- A
.nojekyll
file in the repo root (disables Jekyll so files are served as-is). - A
404.html
file in the same folder as yourindex.html
file (due to the way navigation works in stlite, this ensures users won’t see a 404 page not found error when refreshing while on a subpage of your app, or if they share or bookmark a link to a subpage) - If
mode="gh-actions"
:- A workflow file at
.github/workflows/deploy.yml
. - A
PAGES_SETUP.md
helper with the exact steps to enable Pages via GitHub Actions.
- A workflow file at
- If
mode="manual"
:- A
PAGES_SETUP.md
helper with exact steps to configure Deploy from a branch.
- A
Finishing steps (by mode)
These are also given in PAGES_SETUP.md
, but are reproduced here for simplicity
If mode="manual"
Commit
index.html
,.nojekyll
and404.html
In Repo → Settings → Pages:
- Build and deployment → Source:
Deploy from a branch
- Branch: your branch (e.g.,
main
) - Folder:
/docs
(ifuse_docs=True
) orroot
(ifuse_docs=False
)
- Build and deployment → Source:
If mode="gh-actions"
- In Repo → Settings → Pages:
- Build and deployment → Source:
GitHub Actions
.
- Build and deployment → Source:
Make sure you enable this before committing the files!
If you do not, it will not trigger a build when first enabled without you forcibly making a change to index.html
and committing/pushing that change.
- Commit the generated files:
.github/workflows/deploy.yml
.nojekyll
404.html
index.html
in your target folder (docs/
ifuse_docs=True
, else repo root).
- Push to your repo. The workflow will build and deploy your site.
Both
Your app will be available at:
https://<your-github-username>.github.io/<your-repo-name>/
Note: It can take a few minutes for the first deployment to appear. If using Actions, ensure the workflow runs and passes; if using manual mode, double-check your Pages settings.
Optional - add a link to your deployed app to the ‘About’ section of your repository
Once you have followed the instructions, head over to the ‘about’ section of your repository and click on the cog.
Tick ‘Use your GitHub Pages website’ - your will likely take the form ‘https://your-username.github.io/your-repository-name’
Then click ‘save changes’
You can then click the link to head to your hosted app.
If the app has built successfully, there will be a green tick next to your last commit message:
Next Steps
- Experiment with adding extra files (e.g., a
.streamlit/config.toml
for customization). - Share your packaged app on GitHub Pages.
- Contribute back by reporting issues or suggesting improvements!