Skip to content

@stlite/browser

@stlite/browser allows you to mount a Streamlit app on a static web page using a <script> tag.

You can use Stlite on your web page by loading the JavaScript and CSS files via <script> and <link> tags.

The easiest way is using the <streamlit-app> custom element.

custom-element.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - Custom Element</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js"></script>
</head>
<body>
<streamlit-app>
import streamlit as st
st.title("Custom Element Test")
option = st.selectbox(
'How would you like to be contacted?',
('Email', 'Home phone', 'Mobile phone'))
st.write('You selected:', option)
</streamlit-app>
</body>
</html>

You can load the app from an external Python file using the src attribute:

src-attribute.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - Source Attribute</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js"></script>
</head>
<body>
<streamlit-app src="./app.py"></streamlit-app>
</body>
</html>

The filename is extracted from the URL path (e.g., ./app.py becomes app.py in the virtual filesystem).

You can also combine the src attribute with child elements to add additional files and requirements:

<streamlit-app src="./main.py">
<app-file name="utils.py" url="./utils.py"></app-file>
<app-requirements>
numpy
pandas
</app-requirements>
</streamlit-app>

For more control, you can use the mount() function.

mount.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - Basic Mount</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
</head>
<body>
<div id="root"></div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js";
window.controller = mount(
{
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
st.title("Stlite Browser Test")
name = st.text_input('Your name')
st.write("Hello,", name or "world")
`,
},
},
document.getElementById("root"),
);
</script>
</body>
</html>

You can create multipage apps by mounting multiple files, including an entrypoint and page files in a pages/ directory.

multipage.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - Multipage App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
</head>
<body>
<div id="root"></div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js";
mount(
{
entrypoint: "Hello.py",
files: {
"Hello.py": `
import streamlit as st
st.title("Main page")
st.write("Welcome to the multipage app demo!")
st.write("Use the sidebar to navigate between pages.")
`,
"pages/1_Page1.py": `
import streamlit as st
st.title("Page 1")
st.write("This is the first page.")
`,
"pages/2_Page2.py": `
import streamlit as st
st.title("Page 2")
st.write("This is the second page.")
`,
},
},
document.getElementById("root"),
);
</script>
</body>
</html>

Use the requirements option to install Python packages.

requirements.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - Requirements</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
</head>
<body>
<div id="root"></div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js";
mount(
{
requirements: ["faker"],
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
from faker import Faker
# Set seed for deterministic output (useful for testing)
Faker.seed(12345)
fake = Faker()
st.title("Requirements Demo")
st.write("This demo shows how to install Python packages.")
st.subheader("Faker Examples")
st.write("Name: " + fake.name())
st.write("Email: " + fake.email())
st.write("Address: " + fake.address().replace("\\n", ", "))
st.write("Company: " + fake.company())
`,
},
},
document.getElementById("root"),
);
</script>
</body>
</html>

You can mount multiple independent Stlite apps on the same page by calling mount() multiple times with different container elements. By default, each app runs in its own Web Worker with isolated session state.

When embedding multiple apps, set disableDocumentStyles: true to prevent each app from injecting global document styles that could conflict with each other or with your page layout.

multi-app.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - Multi App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
gap: 16px;
width: 100%;
height: 100%;
}
.app {
position: relative;
flex: 1;
}
</style>
</head>
<body>
<div class="container">
<div id="app1" class="app"></div>
<div id="app2" class="app"></div>
</div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js";
mount(
{
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
st.write("Hello, stlite!")
st.write("App 1")
if "counter" not in st.session_state:
st.session_state.counter = 0
if st.button("Increment counter"):
st.session_state.counter += 1
st.write(f"Counter: {st.session_state.counter}")
`,
},
disableDocumentStyles: true,
},
document.getElementById("app1"),
);
mount(
{
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
st.write("Hello, stlite!")
st.write("App 2")
if "counter" not in st.session_state:
st.session_state.counter = 0
if st.button("Increment counter"):
st.session_state.counter += 1
st.write(f"Counter: {st.session_state.counter}")
`,
},
disableDocumentStyles: true,
},
document.getElementById("app2"),
);
</script>
</body>
</html>

You can also combine multiple apps with sharedWorker: true so they share a single Python worker, reducing memory usage while keeping session state independent.

multi-app-shared-worker.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - Multi App SharedWorker</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
gap: 16px;
width: 100%;
height: 100%;
}
.app {
position: relative;
flex: 1;
}
</style>
</head>
<body>
<div class="container">
<div id="app1" class="app"></div>
<div id="app2" class="app"></div>
</div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js";
mount(
{
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
st.write("Hello, stlite!")
st.write("Shared Worker App 1")
if "counter" not in st.session_state:
st.session_state.counter = 0
if st.button("Increment counter"):
st.session_state.counter += 1
st.write(f"Counter: {st.session_state.counter}")
`,
},
sharedWorker: true,
disableDocumentStyles: true,
},
document.getElementById("app1"),
);
mount(
{
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
st.write("Hello, stlite!")
st.write("Shared Worker App 2")
if "counter" not in st.session_state:
st.session_state.counter = 0
if st.button("Increment counter"):
st.session_state.counter += 1
st.write(f"Counter: {st.session_state.counter}")
`,
},
sharedWorker: true,
disableDocumentStyles: true,
},
document.getElementById("app2"),
);
</script>
</body>
</html>

By default, the virtual file system is in-memory and lost on page reload. Use the idbfsMountpoints option to mount directories backed by IndexedDB, so files persist across page reloads.

idbfs.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - IDBFS Persistent Storage</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
</head>
<body>
<div id="root"></div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js";
mount(
{
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
st.title("IDBFS Persistent Storage")
st.write("Data written to \`/mnt\` persists across page reloads using IndexedDB.")
try:
with open("/mnt/counter.txt", "r") as f:
count = int(f.read())
except (FileNotFoundError, ValueError):
count = 0
count += 1
with open("/mnt/counter.txt", "w") as f:
f.write(str(count))
st.write(f"Visit count: {count}")
if st.button("Reset counter"):
with open("/mnt/counter.txt", "w") as f:
f.write("0")
st.rerun()
`,
},
idbfsMountpoints: ["/mnt"],
},
document.getElementById("root"),
);
</script>
</body>
</html>

To reduce memory consumption when running multiple Stlite apps, enable SharedWorker mode.

shared-worker.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Stlite Demo - SharedWorker</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.css" />
</head>
<body>
<div id="root"></div>
<script type="module">
import { mount } from "https://cdn.jsdelivr.net/npm/@stlite/[email protected]/build/stlite.js";
mount(
{
sharedWorker: true,
entrypoint: "streamlit_app.py",
files: {
"streamlit_app.py": `
import streamlit as st
st.title("SharedWorker Demo")
st.write("This app runs in SharedWorker mode.")
st.write("Multiple Stlite apps can share the same Python worker to reduce memory usage.")
if "count" not in st.session_state:
st.session_state.count = 0
if st.button("Click me"):
st.session_state.count += 1
st.write(f"Button clicked {st.session_state.count} {'time' if st.session_state.count == 1 else 'times'}")
`,
},
},
document.getElementById("root"),
);
</script>
</body>
</html>

Mounts the Streamlit app to the given HTML container.

Parameters:

  • options: object | string
    • If a string is provided, it is treated as the content of the streamlit_app.py file.
    • If an object is provided, it must be a MountOptions object (see below).
  • container: HTMLElement (optional, default: document.body)
    • The DOM element to mount the app into.

Returns:

A StliteController object with the following methods:

  • unmount(): Unmounts the app and disposes the kernel.
  • install(requirements): Installs additional packages.
  • writeFile(path, data, opts): Writes a file to the virtual filesystem.
  • readFile(path, opts): Reads a file from the virtual filesystem.
  • unlink(path): Deletes a file.
  • renameFile(oldPath, newPath): Renames a file.

Configuration object for mount().

PropertyTypeDescription
entrypointstringThe file path of the main script (e.g., "streamlit_app.py"). Required if files is an object.
filesRecord<string, FileContent>An object mapping file paths to their content. Content can be a string, Uint8Array, or an object { url: string }.
requirementsstring[]A list of Python packages to install at startup.
streamlitConfigRecord<string, any>Streamlit configuration options (e.g., { "client.toolbarMode": "viewer" }).
archivesArchiveObject[]List of archive files (e.g., zip) to download and unpack.
pyodideUrlstringCustom URL for the Pyodide interpreter.
sharedWorkerbooleanWhether to use a SharedWorker.
idbfsMountpointsstring[]List of paths to mount with IDBFS (IndexedDB-based persistent file system).

The <streamlit-app> custom element provides a declarative way to embed Streamlit apps.

AttributeDescription
srcURL to load the main Python file from. The filename is extracted from the URL path.
shared-workerEnable SharedWorker mode for reduced memory usage.
ElementDescription
<app-file name="..." [url="..."] [entrypoint]>Define a file. Use url to load from URL, or place content inside the tag. Add entrypoint to mark as the main script.
<app-requirements>List Python packages to install (one per line, like requirements.txt).
<app-archive url="..." format="...">Load and unpack an archive file (zip, tar.gz).
  1. Inline code: Place Python code directly inside <streamlit-app>:

    <streamlit-app>
    import streamlit as st
    st.write("Hello")
    </streamlit-app>
  2. External file via src: Load from a URL:

    <streamlit-app src="./app.py"></streamlit-app>
  3. Child elements: Use <app-file> with entrypoint attribute:

    <streamlit-app>
    <app-file name="app.py" entrypoint>
    import streamlit as st
    st.write("Hello")
    </app-file>
    </streamlit-app>