Stlite supports writingDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/whitphx/stlite/llms.txt
Use this file to discover all available pages before exploring further.
await expressions directly at the top level of your Streamlit script — outside of any async def function. This is necessary because the browser runtime that Stlite runs on has a fundamentally different execution model from standard Python, and the usual asyncio.run() call simply does not work. Understanding when and why to use top-level await will help you write correct async code in your Stlite apps.
Why asyncio.run() doesn’t work
In a standard server-side Streamlit deployment, your script runs in a normal Python process. If you need to call an async function, you can wrap it with asyncio.run(), which creates a fresh event loop, runs the coroutine to completion, and then exits:
asyncio.run() inside this environment tries to start a second event loop on top of the already-running one, which raises a RuntimeError.
The solution is top-level await: Pyodide’s execution model allows await at the top level of a script, suspending the script cooperatively on the running event loop rather than trying to spin up a new one.
Top-level
await is a Stlite-specific feature. The same code will not
run in a standard Streamlit server deployment. If you need to share code
between both environments, wrap your async calls inside async def functions
and use asyncio.run() conditionally, or use synchronous alternatives where
available.Example 1: asyncio.sleep()
time.sleep() is a no-op in Stlite (see Limitations). It returns immediately without actually pausing execution because a true blocking sleep would freeze the browser’s event loop and lock up the entire tab.
The correct replacement is asyncio.sleep(), which pauses the script cooperatively without blocking the event loop.
Direct top-level await:
async def function and await it at the top level. This is useful when you want to group related async operations together:
Example 2: pyodide.http.pyfetch()
Pyodide provides pyodide.http.pyfetch(), an async wrapper around the browser’s native Fetch API. Since it is an async function, you must await it. Top-level await makes this concise:
response.json() and response.string() as awaitable methods for common response formats. See the HTTP Requests guide for a full walkthrough of the available networking options.
st.spinner() with blocking operations
Stlite runs in a single-threaded environment. When a blocking operation occupies the event loop, no other code — including the code that renders the spinner — gets a chance to run. This means st.spinner() will not visually appear if you call a blocking method immediately inside the with block.
The problem:
asyncio.sleep(0.1):
Adding a short async sleep before the blocking call gives the event loop a chance to render the spinner UI before execution is handed back to the blocking method:
The
await asyncio.sleep(0.1) adds a real 0.1-second delay to your
execution. This is an intentional trade-off to allow the spinner to appear.
For most use cases the slight delay is imperceptible.st.spinner() — always add await asyncio.sleep(0.1) before the blocking call when you need the spinner to display.