website/docs/publish/web/static-website/index.md
import TabItem from '@theme/TabItem'; import Tabs from '@theme/Tabs';
Instructions for publishing a Flet app as a standalone static website (SPA) that runs entirely in the browser with Pyodide. No Python code runs on the server.
Pyodide is a port of CPython to WebAssembly (WASM) and has some limitations.
:::note[Native Python packages]
Native Python packages (vs "pure" Python packages written in Python only) are packages
partially written in C, Rust, or other languages producing native code.
Example packages are numpy, cryptography, lxml, pydantic-core.
Pyodide comes with a list of built-in packages. To use a package from PyPI, it must be pure Python or provide a wheel built for Emscripten. :::
:::note[Async and threading] Static websites run in a single browser thread. You can use sync and async handlers, but long-running CPU work or blocking calls will freeze the UI. Prefer async I/O, or move heavy work to a server and call it via a web API. :::
:::tip[Micropip] Pyodide installs packages with micropip. You can use the Micropip API directly in your Flet app:
import sys
if sys.platform == "emscripten":
import micropip
await micropip.install("regex")
:::
There are two ways to publish a static website: flet build web and flet publish.
Both produce a static site that runs in the browser via Pyodide. They
differ mainly in how and when Python dependencies are installed:
flet publish | flet build web | |
|---|---|---|
| Flutter required | No | Yes |
| Dependency install | At runtime, in the browser (micropip) | At build time, on your machine (pip) |
| Build time | Faster — no Flutter compilation, no local pip install | Slower — Flutter build + local dependency install |
| Initial load time | Slower — wheels are fetched from PyPI on page load | Faster — dependencies are already bundled |
| Pure-Python wheels | ✅ | ✅ |
| Pyodide-built binary wheels | ✅ (from Pyodide CDN) | ✅ (auto-detected from Pyodide registry) |
| Source distributions (sdists) | ❌ micropip can't build sdists in the browser | ✅ pure-Python sdists, opt in via source_packages |
A common failure mode with flet publish is a (transitive) dependency that ships only a
source distribution (.tar.gz, no wheel) — for example,
docopt. micropip cannot build
sdists, so load fails with:
ValueError: Can't find a pure Python 3 wheel for '<package>'
In such cases, we suggest switching to flet build web and
adding the package to source_packages.
Note that source_packages only works for pure-Python sdists. Sdists with C/Rust
extensions (e.g. numpy, cryptography) cannot be built for Pyodide — use Pyodide's
built-in packages instead.
flet publishDoes not require Flutter. It packages your app and installs dependencies in the browser at runtime via micropip.
To publish an app, run:
flet publish <path-to-app.py>
The website is published to --distpath (default: ./dist).
You can try published Flet app using flet serve command:
flet serve dist
Then, open http://localhost:8000 in your browser to check the published app.
If the assets directory exists (default: ./assets), its contents are copied
to the published site root. Use --assets to point to a different
folder. Assets are not packaged inside the app.tar.gz.
flet build webUses Flutter and Pyodide.
Dependencies are resolved and installed locally at build time (via pip), then bundled into the output archive.
:::tip[Note] Complementary and more general information is available here. :::
flet serve serves the default
output directory (./build/web):
flet serve
These settings apply to flet build web and flet publish, unless noted.
Use a base URL when hosting your app in a subdirectory. Flet normalizes it to
/<value>/ and uses / when unset.
Its value is determined in the following order of precedence:
--base-url[tool.flet.web].base_url"/"Controls how routes are represented in the URL:
path - clean paths; requires SPA-capable hosting.hash - uses the URL hash; works on static hosts without SPA support.Its value is determined in the following order of precedence:
--route-url-strategy[tool.flet.web].route_url_strategy"path"Selects the Flutter web renderer:
auto (default) - let Flutter choose the best renderercanvaskitskwasmIts value is determined in the following order of precedence:
--web-renderer[tool.flet.web].renderer"auto"By default, Pyodide, CanvasKit, and fonts are loaded from CDNs to keep the output small. Disable CDN loading for offline or air-gapped deployments.
CDN loading is disabled in the following order of precedence:
--no-cdn[tool.flet.web].cdn = falseConfigure PWA colors used in manifest.json and browser UI.
For each setting:
--pwa-background-color / --pwa-theme-color[tool.flet.web].pwa_background_color / [tool.flet.web].pwa_theme_color#FFFFFF / #0175C2By default, flet build web enables Flutter's WASM output.
:::note[Note]
flet build web only.
:::
The WASM output is disabled in the following order of precedence:
--no-wasm[tool.flet.web].wasm = false